今日は、API使用時における、データ型にまつわる注意点について説明したいと思います。以下のWindows APIを見てください。このGetTickCount関数は、システムが起動してからの経過時間をミリ秒単位で、返却するAPI仕様となっています。APIは、C言語で記述されている場合がほとんどなので、以下のようにC言語による表現が、一般的です。戻り値であるDWORD型は、C言語では、4バイトの符号無し整数値で、取りうる値の範囲は、0 ~ 4294967296 となります。
DWORD GetTickCount();
細かい話しが好きなヒトは是非、読んでください。1時間が3600秒、ミリ秒で表現すると、3600000ミリ秒=3600×1000、これを1日に換算すると1日は、86400000ミリ秒=24×3600000となります。APIで返却されるDWORDの最大値が4294967296なので、この値を86400000ミリ秒で割ると、約49.71日。すなわち、システムが起動してから連続50日ぐらいまで、利用できるAPIということになります。49.71日を超えるとどうなるかというと、おそらく0にリセットされ再カウントされます。試してませんので、あしからず。C言語は、変数の限界値を超えるとオーバーフローではなく、サイケリックに0リセットされます。ちなみにパソコンを再起動させたときにも、0リセットされる仕様です。
このAPIをVBAで使用するには、以下のように宣言してやります(DllGetTickCountの箇所は、好きな関数名に変更できます)。ここで注意すべきは、VBAでは、4バイトの符号無しのデータ型が存在しないことです。ですので、ここでは、4バイトの符号有りのLong型で、DWORD型を代用してやります。ちなみに、Long型で扱える数値の範囲は、-2147483648 ~ 2147483647 です。
Public Declare Function
DllGetTickCount Lib "kernel32.dll" Alias "GetTickCount" () As Long
C言語側で開発されたこのAPIは、システム起動からの経過時間を 0 ~ 4294967296 の値で返却しますが、受け取ったVBA側は、 -2147483648 ~ 2147483647 の値として認識してしまいます。ですので、ここに不都合が生じてしまいます。ですが、VBA側で少し、工夫してやることで、この不都合を解消することができます。以下が、その改善例です。
Public Function VbaGetTickCount() As Currency
Dim v_long As Long
Dim v_cur As Currency
Dim v_hex_str As String
Dim dgt1 As Currency
Dim dgt2 As Currency
Dim dgt3 As Currency
Dim dgt4 As Currency
v_long = DllGetTickCount()
'正の場合
If v_long >= 0 Then
'そのまま返却してOK
VbaGetTickCount = v_long
Exit Function
'負の場合
'2147483648よりも大きい値が、VBA側ではマイナスとなってしまいます。
Else
'*** 取得したLong値を8桁の16進文字列に変換 ***
'8桁に補正
v_hex_str = WorksheetFunction.Dec2Hex(v_long)
v_hex_str = Right("00000000" & v_hex_str, 8)
'1バイトごとに10進値としてCurrency型に格納
dgt1 = WorksheetFunction.Hex2Dec(Mid(v_hex_str, 7, 2))
dgt2 = WorksheetFunction.Hex2Dec(Mid(v_hex_str, 5, 2))
dgt3 = WorksheetFunction.Hex2Dec(Mid(v_hex_str, 3, 2))
dgt4 = WorksheetFunction.Hex2Dec(Mid(v_hex_str, 1, 2))
'再計算して符号無しの値として返却。
'Currency型で演算することで、正の値として表現できます。
v_cur = dgt1 + _
(dgt2 * 256) + _
(dgt3 * 256 * 256) + _
(dgt4 * 256 * 256 * 256)
End If
VbaGetTickCount = v_cur
End Function
負の値が返った場合(Else文)に注目して欲しいのですが、ここでは、一旦、返却値を16進文字列に変換して、4バイトの各バイトの値をそれぞれ数値に変換し、Currency型で再計算することにより、API側が意図した正の値に変換するようにしてあります。
符号ビットを考慮した数値演算による変換方法も考えたのですが、意外と今回のこれが、理解しやすく、単純で良いのではないでしょうか。
C言語で使用できるデータ型のすべてに、VBA側のデータ型が対応しているわけではありませんので、APIを使用すると、このように、一手間加えなければ、いけない場合がありますので、注意してください。
実は、既に公開しているオープンソースなどの、ソースコードにGetTickCount関数を使っている所があったのですが、最近、バグっていることに気付いたんです。こっそり、直して、アップしているので、以前のものを使っている方がいたら、差し替えをお願いします。本当に申し訳ありませんでした。聖書の教えによると、『ヒトは、やましいことがあると、それを隠す傾向にある』そうです。
そのせいでしょうか? なんか少し、字が小さくなっているのは…
今回は、サンプルソースはございません。
ご意見・ご要望等ありましたら、画面最下部のメールアドレスまでご連絡ください。