アマプログラマーの独り言


VBのコールバック関数へ文字列を渡す方法

2002年03月31日
例えばVC++なんかでDLLファイルを作って、それをVBからAddressOfで関数ポインタを渡し、その関数、つまりコールバック関数へ文字列を渡す方法です。

ちなみにちょっと無理矢理っぽいです。
既に知ってたらごめんなさい。

ではコードです。

-----------------------------------------------------
VC++側

// 宣言とかは適当に合わせてください

int CALLBACK CallBack(int *VB_Func)
{
  int (*Func)(long, int);
  char *str = "ABCDEFG";

  Func = (int(*)(long, int))VB_Func;
  Func((long)str, 8);
  return 0;
}

-----------------------------------------------------
VB側

' ここも型とかは適当に合わせてください

' API宣言
' VC++製
Private Declare Function CallBack Lib "xxx.dll" (ByVal VB_Func As Long) As Long
' MoveMemory(宣言はプロトタイプと異なってます)
Private Declare Function MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As String, ByVal Source As Long, ByVal Length As Long) As Long


' メイン
Public Sub Main()

Call CallBack(AddressOf Func)

End Sub

' コールバック関数
Public Function Func (ByVal p As Long, ByVal Length As Long) As Long
Dim tmp As String

tmp = String$(Length, vbNullChar)
Call MoveMemory(tmp, p, Length)

Debug.Print tmp
End Function

-----------------------------------------------------
結果(イミディエイト)

ABCDEFG

-----------------------------------------------------


まずVC++側ではchar型へのポインタをlong型にキャストし、アドレスを直接渡します。
VB側では貰ったそのアドレスを、MoveMemoryへ渡します。
MoveMemory関数内ではそれを「ポインタ」だと処理し、VC++側で設定した「ABCDEFG」という文字列を参照してくれます。
コールバック2番目の引数は最終NULL文字を含めた長さです。直接数値を渡してますがstrlen()関数でもOKです。


では何故こんなメンドイやり方をしているかと言うと、それはVBの「String型」のせいです。
VBの文字列型の初期サイズは0バイトです。つまり領域が0なんです。
API関数を使う場合、必ず領域の確保を行わなければなりません。
当然引数にサイズを指定する事はできないので、もしString型で受け取ろうとすると「VB6が原因でxxx.dll内にエラーが起きました」という危なっかしいエラーと共にVBが落ちるでしょう。

Cなどでは「ポインタ」という便利なもの(しかし難しいもの)があるので、こういう問題はおきません。
他の言語は知りません。