意の中のカワズ(35歳の壁 別館)

35歳の壁の別館ブログです。コード中心になるようにしたいので、技術雑記はできるだけ本館に書きます。

VBA:TIPS 設定を読み取る

ExcelVBA を書いているとき、というか、コード書きはじめのとき私は最初に設定ファイルを読み込ませるコードをよく書きます。

設定ファイルといってもExcel なので、シートの中に書いてしまえばいいので、それを読み取るわけです。
これをうまく活用すると設定からプログラムを呼び出したり、プロパティ操作をするようなFWが作れたりします。

大したコード、大した内容ではないのですが、さくっとツールを作りたいときには、設定をConst にしたりして終わらせてしまう人もいるかもしれませんが、拡張していくにつれて同じ値の取得に似たようなコードをずらずら書くことになります。

定数や値の取得には、enumVBAは使えますので、本当はこの辺をうまく拡張してクラスファイル化すると便利だったりしますが、タダのツールで・・という程度の時に書くコード。そんな感じのもの。

続きを読む

VBS:マウスパッドの有効無効をキーボードだけで切り替える 続編

前回紹介したやり方は、簡単なバッチ(プログラムのようなもの)を作って、それを起動する設定までを含めた手順で分かりづらかったので手っ取り早く、ファイルをアップしてみました。
ただ、残念なことにこのブログにファイルがアップできなかったので、こちらのブログとは別の本館ブログ(プログラミング以外も書いているブログ)に配置することにいたしました。


利用前に必ずマウスをつないだ状態で行ってください。
実行するとタッチパッドの有効無効が切り替わりますので、マウスカーソルが動かせなくなります。
あくまで、実施は自己責任でお願いします。

※ 間違えて開始してしまった場合には、[Windowsキー] → [設定(歯車マーク)] → [デバイス] → [タッチパッド] の順番に開くと設定が変更できます。 

続きを読む

VBA:「動的配列未初期化のチェック」

疲れた・・。
しようもない無いところでコケタ。

例えば、


Type userTeigiA
strA() as string
lngB(10) as long
intC as integer
End Type

なんてのがあるとする。
構造体とは呼ばないんだよね・・ユーザ定義だっけ。

で、この構造体(って、書いてたらごめん)・・・じゃない、ユーザ定義(苦笑)の中に動的配列が入れられるかどうか。

なんて、あまり考えなかったけどできるわけです。
が、strA に対して当然何か入れたいわけです。

当然、そのときは最大の値を使ってRedim するわけですが、この最大値が取れない。

strA は構造体の中にあるとはいえ、動的配列です。
動的配列と静的配列の最大の違いは、

UBound で要素数が取れるかどうか。

です。

えぇ、取れません。


dim test as userTeigiA

intSize = UBound(test.strA)

なんて、書こうものなら

「実行エラー:'9' インデックスが有効範囲にありません。」

なんてことを言われる。
そう、いつもなら「あぁ・・・未初期化の動的配列を参照してしまう処理を書いてしまった・・。」

そう思っていたでしょう。
でも、今日は違いました。

「てやんでぃ!何が何でもチェックしてやる!」

と思いました。いや、こういう日が来ないと成長しないんで・・。


とりあえず、これまでにも回避術はいくつか覚えました。
最も有効なのは、
・動的配列が使えることを忘れる(w)

ついで
・事前に必ずRedimでサイズゼロ指定する処理フローで書く。

ついで
・必ずRedimした後に使用する処理フローで書く。

ついで、
・コレクションにしてしまう。(w)


いえ、最後のが正解なんですが、Collection で何でもかんでもやりすぎるのが
嫌になってくるので、そろぞろ本気でどうにかして

UBOUND(未初期化動的配列)

を成功させようと思うわけです。
調べました。

ちなみに

dim intSize as integer
dim strA() as string

on error resume next
intsize = ubound(strA)
redim strA(intsize+1)


なんていう、エラーキャッチ系の処理は、個人的な脳内規約でノーなのでナシです。
こういうの好きじゃありません。
(いや・・・スゴイ簡単でいいんじゃないの・・。苦労しないし 苦笑)


動的配列の未初期化状態でのアクセスは書かない癖がついてるので、構造体の内部で動的配列をメンバとした場合のキーワードで探してしまい、スゴイ遠回りをしましたが、結論。


未初期化動的配列 の初期化済みチェックを行う方法。

的な感じで探せばよかった・・orz

うまく当たらないと出てこないかもしれませんが、

・SafeArrayAllocDescriptor
または、
・GetMem4

って、APIを使います。
SafeArrayAllocDescriptorは試してませんが、たぶんこれは強制的にアロックさせるんじゃないかと思うんですね。
であれば、Redim 未初期化動的配列(0) してるのと同じです。

ので、GetMem4 を使いました。

例:


Private Declare Sub GetMem4 Lib "msvbvm60" (ByRef Addr() As Any, RetVal As Long)

Sub testGetMem4()
Dim strA() As String
Dim lngVal As Long

GetMem4 strA, lngVal

If lngVal <> 0 Then
MsgBox "初期化してある"
Else
MsgBox "初期化してない"
End If
End Sub

こんなかんじ。
最初使ったときは、てっきり初期化された要素数が還ってくるのだと思っていました。
が、初期化済み動的配列に使うと、無茶苦茶デカイ数字が還ってきます。

で、これは・・・どうつかう??。
って、思ったけど「あ、4バイトメモリを取得。」
アドレスか・・。

てなわけで、配列がメモリを割り当てられているかどうか。をチェックできんじゃないかと。
(全然調べてないやん。w)

なので、0 じゃなければ、アドレスアリ=初期化済み と判断してるわけです。
気になるのは、
・4バイトアドレス空間
・msvbvm60 というライブラリ
の2つですね。

何処でも使えるものではないかもしれない・・。
ちなみに他にもいろいろあって、SafeArrayGetDimってのは、


Public Declare Function SafeArrayGetDim Lib "oleaut32" _
(ByVal pa As Long) As Long


と宣言するので、oleaut32 ってヤツが、何者か気になります。
どこでも入ってるやつか?

あとは、RtlMoveMemory が似たようなことが出来る模様。


Declare Sub RtlMoveMemory Lib "KERNEL32.dll" (hpvDest As Any,
ByVal hpvSource as Long, ByVal cbCopy as Long)

とあるので、これは kernel32.dll からの呼び出しですからなかなか広く使えそうですね。

まぁ、個人的には動けばよいのですが。


そんな感じです。
ただ、これらAPIを使ってチェックするメソッドを作成しようとして、


Private Declare Sub GetMem4 Lib "msvbvm60" (ByRef Addr() As Any, RetVal As Long)

sub testIsMoveArrayEmpty()
Dim strA() As String

if not isMoveArrayEmpty(strA) then
msgbox "初期化してねー"
end if
end sub

function isMoveArrayEmpty(obj as object ) as boolean

Dim lngVal As Long

GetMem4 obj , lngVal

If lngVal <> 0 Then
isMoveArrayEmpty = True
Else
isMoveArrayEmpty = false
End If
End Sub


なんぞしてみると・・・w
当然、アドレスがないものをオブジェクト変数(の参照アドレス)にアドレス設定できるわけないんで、エラーだよ!

って言われます。
次は、コレだな。w

渡せないものを渡してみたい・・w

長文になっちゃった。

どうでもいいけど、ハテナさん。

<PRE>
</PRE>

でくくった場合、</PRE>の前に改行があると</PRE>の後ろの行を</PRE>の前に持ってくるのは・・・なんぞね??

Excel関数:「曜日を関数だけで表示」

また、忘れそうなので書いておくと
B6 にシリアル値またはそれに該当する日付情報があるとして、


=TEXT(B6,"aaa")

とすればOK。

要するに書式のフォーマットを表示変換に利用してるわけですが、
後ろの aaa ってのと、関数名を忘れそうになる。

ヘルプには aaa が何かはノッテイナイ。
調べるには、マクロで書式設定当たりを抜くのがよいだろう。

ついつい、Weekday を使おうとするが、あれは数値しか返さないので
CHOOSE関数かIF文の荒らしでいくかしないとだめ。

Choose関数ならこうなる。


=CHOOSE(WEEKDAY(B6),"日","月","火","水","木","金","土")

便利なんだかめんどくさいんだか・・。

VBA:「すべての図形を検索」

まぁ、当然開発中に必要になるのがこういうヤツですね。
以下3つの投稿を組み合わせているのでおまけで載せときます。

追記:@2011.09.30 16:30
 Doevent を挟んでもシェイプの選択描画がうまくいかなかったので、
 該当セルを着色してどのセルの確認かを見やすくしました。
 他、ちょっとSelectionアイテムに対して処理することでサンプルを
 読みやすくしました。



' シェイプを全て探します表示します。
' ただしこれは全検索タイプなので遅い
Public Function srchShapeAllWithDelete() As Boolean

Dim objShp As Shape
Dim valRGBIndex as Variant

For Each objShp In ActiveSheet.Shapes
Range(objShp.TopLeftCell.Address(False, False)).Activate
Range(objShp.TopLeftCell.Address(False, False)).Select
valRGBIndex = Selection.Interior.ColorIndex
Selection.Interior.ColorIndex = 3 ' 赤で着色
If msgBox(objShp.TopLeftCell.Address(False, False) & _
" にある " & objShp.Name & vbCr & _
"このシェイプを消しますか?", vbYesNo) = vbYes Then

objShp.Delete
End If
Selection.Interior.ColorIndex = valRGBIndex
Next

End Function

VBA:「行列番号を指定して、セル名を返す」

カラム番号 To カラム名 ってことです。
よく使うので書いておきます。


' セル番号に該当するセル名に返します
Public Function rangeToColmunName(lngRow As Long, intCol As Integer) As String

Dim strBuf As String

strBuf = Cells(lngRow, intCol).Address(True, False)
rangeToColmunName = Mid(strBuf, 1, InStr(1, strBuf, "$") - 1)

End Function

なんか、こういうアナログ(?)な処理で無理矢理変換する系の
Commonsが自分の場合多いです。