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

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

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>の前に持ってくるのは・・・なんぞね??