DLL から Visual Basic 内の関数をコールする


 僕がプログラムを作る際、Visual C++ と Visual Basic を適材適所に用います。C言語で考えた方が素直になる場合、パフォーマンスが必要とされ、無駄を極力少なくしたい処理などは Visual C++、逆に、画面周りなどは Visual Basic で簡単に作ってしまうという風に。制御系のプログラムやそれをモニタするプログラム、又は、シミュレータ等をよく作るので、API をコールする場合も多く、また、マルチスレッドや COM ポートの制御やバッファリング等をあらわすには、C言語の方が適していると思われます。よって、この部分のロジックが重要で、画面周りはそれほど大切でない場合などが多くあり、先に記した方法がやり易いということになります。

 そこで、よく用いる方法が Visual C++ を使用して DLL を作成し、Visual Basic で作成したプログラムがその DLL を呼び出すという方法です。通常、Visual Basic 側から DLL を呼び出しますが、つい最近、DLL 側から Visual Basic の関数を呼び出したいケースが現れたので、そのやり方をここに示します。

 状況としては、デバイスにアクセスする API を呼び出したいが、いつ API から制御が戻ってくるかわからないので、その間、画面がフリーズするのはままずい。で、子スレッドでその API を呼び出し、親スレッドはすぐにリターンする。ここで問題になるのが、Visual Basic 側でいつ API の処理が終わったをどうやって知るかということです。その方法として、先のコールバックを用いる方法の他に、Visual Basic からポーリングのようなことをする方法や、ユーザ定義メッセージを使うことなどを考えましたが、タイムラグを少なくすることは、システムに負荷がかかる事につながるからポーリング方法は見送り、Visual Basic 側では凝ったことを極力したくないということで、ユーザ定義メッセージのやり方も見送りました(ところで、Visual Basic でユーザ定義メッセージは受信できるのだろうか・・・)。

 前置きはこの辺にしておいて、以下に方法を示します。Visual C++ にる DLL の作成方法の具体的なコーディング方法は省略します。

 まずは、DLL 側から。DLL からコールしたい関数の方と引数を宣言します。

    typedef int (WINAPI *MyFuncType )( BSTR* data )

 戻り値は int 型、引数は short のポインタ型、つまり、Unicode の文字型ポインタです。WINAPI は ヘッダーファイル windef.h で

    #define WINAPI __stdcall

と定義されています。そして、この関数のアドレスを保管する変数を定義します。

    MyFuncType callback;

Visual Basic の関数ポインタは long で受け取り、上の変数にキャストして保存します。

    DllExport void WINAPI CallFromVB ( long address )
    {
      callback = (MyFuncType)address;
    }

そして、Visual Basic 側の関数を呼び出します。引数の文字列は、Unicode に変換してから渡します。

    BSTR msg;
    int ret;
    msg = SysAllocString( msg, "Test Message", 12 );
    ret = callback( msg );
    SysFreeString( msg );

Visual Basic 側には DLL から呼び出したい関数を .bas ファイルに Global(Public) で定義します。

    Global Function CallFromDLL( ByVal strMsg As String ) As Long

そして、Visual Basic 側から DLL 内の関数を呼び出します。

    Call CallFromVB( Addressof CallFromDLL )

以上の処理で、DLL 内の関数 MyCallBack を Visual Basic から呼び出すことで、Visual Basic 側の関数 CallFromDLL が DLL から呼び出されることになります。

 当初、コールバック関数の定義を

    typedef int ( *MyFuncType )( BSTR* data )

としていて、全然成功しませんでした。そこで、MSDN を調べてみてやっと成功しました。詳細な原因としては、__stdcall と __cdecl の違いをきちんと把握しておらず、今回の Visual Basic では言語仕様適にこの __stdcall と __cdecl の区別がつかず、エラーが発生していたと思われます。


プログラム研究所

TOP-PAGE

Web-Page制作者e-mailアドレス