リストHOME  リストOpen Source

第18回VBAプログラム豆知識

概要 ~ APIとデータ型 ~

 今日は、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関数を使っている所があったのですが、最近、バグっていることに気付いたんです。こっそり、直して、アップしているので、以前のものを使っている方がいたら、差し替えをお願いします。本当に申し訳ありませんでした。聖書の教えによると、『ヒトは、やましいことがあると、それを隠す傾向にある』そうです。
 そのせいでしょうか? なんか少し、字が小さくなっているのは…



第17回  <  >  第19回



ダウンロード

 今回は、サンプルソースはございません。  



連絡先

 ご意見・ご要望等ありましたら、画面最下部のメールアドレスまでご連絡ください。




エクセルバックアップ・ページのフッター
管理者のメールアドレス