● API関数CopyMemoryのすすめ ●

イミディエイトウィンドウに "? 255 * 200" と入力して [Enter] を力いっぱい叩くと、オーバーフローのエラーが表示される。

文字列の上位バイトと下位バイトから2バイト文字、要するに全角文字の文字コード(Unicode値)を算出する以下のコードを書いてみたところ、文字列 "0123456789" あたりで撃沈した。

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:上位バイトと下位バイトからInteger値を算出する
' 引  数:(i)HighByte … 上位バイト
'         (i)LowByte  … 下位バイト
' 返り値:Integer値
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Private Function MakeInteger(ByVal HighByte As Byte, ByVal LowByte As Byte) As Integer
    MakeInteger = CInt((HighByte * &H100) + LowByte)
End Function

とりあえず、

    Call MakeInteger(127, 255)  '値にすると32767 → 通常運転
    Call MakeInteger(128, 255)  '値にすると33023 → オーバーフロー発生

と、実行してくれたまえ。VB の Integer型は2バイトでその範囲は -32768 〜 32767。C言語と違って unsigned を指定することは出来ないので、32767 を超えるとたちまちオーバーフローである。従って上位バイトが 127 を超えていたら、負(マイナス)の方向へ値をひっくり返す処理を記述しなければならない。上記の場合だとInteger型で保持させるには -32513 という値を算出する必要がある。それでは実際にコードを書いてみましょう。

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:上位バイトと下位バイトからInteger値を算出する
' 引  数:(i)HighByte … 上位バイト
'         (i)LowByte  … 下位バイト
' 返り値:Integer値
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Private Function MakeInteger(ByVal HighByte As Byte, ByVal LowByte As Byte) As Integer
    If HighByte > 127 Then
        MakeInteger = (((HighByte - 128) * &H100) Or &H8000) + LowByte
    Else
        MakeInteger = (HighByte * &H100) + LowByte
    End If
End Function

これで問題なしである。上記でも書いた通り、32767 を超えた場合は &H8000(=-32768) を加算して負(マイナス)の世界へ値を反転させ、Integer型としての整合性を強引に保たせている。…でも見た感じ何をしているかさっぱり分からない気がする。

上記の処理を感覚的に分かるように書いてみたい、ということで2秒ほど考えたところAPI関数の CopyMemory を使用するのが簡単であろうという結論に達した。この関数の機能や使い方を理解しているのであれば、感覚的(主に視覚的)に理解できるコードを書くことが出来る。ちなみに CopyMemory というのは仮の別名であって、kernel32.dll には "RtlMoveMemory" という名前で登録されている。注意されたし。

'API宣言
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:上位バイトと下位バイトからInteger値を算出する
' 引  数:(i)HighByte … 上位バイト
'         (i)LowByte  … 下位バイト
' 返り値:Integer値
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Private Function MakeInteger(ByVal HighByte As Byte, ByVal LowByte As Byte) As Integer
    Call CopyMemory(ByVal VarPtr(MakeInteger) + 1, ByVal VarPtr(HighByte), 1)   '上位
    Call CopyMemory(ByVal VarPtr(MakeInteger), ByVal VarPtr(LowByte), 1)        '下位
End Function

Integer型変数の上位に引数の上位バイトを、Integer型変数の下位に引数の下位バイトをメモリコピーしているだけの簡単な処理のはずである。
どうですか? いい感じでしょ?


[参考1]
CopyMemory を使用してRGB値分解処理を色々と書き連ねてみた → 参照されたい!!

[参考2]
ビットマップをPNGやGIFやJPGに変換するより、Long値でオーバーフローが発生するケースの回避コードを転載。
CopyMemoryを使用したコードに改造などしてみてはどうでしょうか?

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:RGB値をARGB値に変換する
' 引  数:(i)RGBValue … RGB値
'         (i)Alpha    … Alpha値(省略時は0(=黒)が設定される)
' 返り値:ARGB値
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Function RGB2ARGB(ByVal RGBValue As Long, Optional ByVal Alpha As Byte = vbBlack) As Long
    Dim R&, G&, B&, A As Long

    'RGB値をR、G、Bに分解
    R = RGBValue And &HFF&
    G = (RGBValue And &HFF00&) \ &H100&
    B = (RGBValue And &HFF0000) \ &H10000

    '桁あふれ対策
    If Alpha > 127 Then
        A = (Alpha - 128) * &H1000000 Or &H80000000
    Else
        A = Alpha * &H1000000
    End If

    RGB2ARGB = (B * &H1) Or (G * &H100&) Or (R * &H10000) Or A
End Function

戻る