マネージドDLL(.NETのクラスライブラリ)をC言語やC++から呼ぶ場合

消えると困るのでコピペ


コピペ元

========================================

VB.NET  DLL側のプログラム

プロジェクト名は vb_dllでクラスライブラリ作成( vb_dll.dllがビルドされる )

プロジェクト → vb_dllプロパティの設定が必要です
(この設定によりビルドと同時にレジストリに登録されます)

注意:regsvr32.exe, regasm.exeを使って登録すると登録エラーか DLLとの接続に失敗してしまいました
オプション設定に問題があったのかもしれませんが原因不明です(^^;;;

プログラムソース サンプル
プログラムソース( Class1.vb )

Imports System.Runtime.InteropServices
<ClassInterface(ClassInterfaceType.None)> _
Public Class Class1
  Public Function vb_function(ByRef aa As String, ByRef bb As String, ByVal xx As Long, ByRef cc As String) As  Integer
    Console.WriteLine("This id vb_dll.vb_function {0}", aa)
    Console.WriteLine("This id vb_dll.vb_function {0}", bb)
    cc = aa + bb
    xx = 888
    Return 0
  End Function
End Class 

ビルドして vb_dll.dllが作成されたことを確認
同時にレジストリに登録されたことを確認 → HKEY_CLASSES_ROOT\vb_dll.Class1


DLLを呼ぶ側のプログラム

上記で作成したマネージドDLL( vb_dll.dll または cs_dll.dll )をアンマネージドプログラムで呼びます
開発言語は C++とします 処理手順は下記の通りです

1.ActiveX( COM )方式(昔のOLE)で呼び出します(レジストリから情報を取得します)
2.レジストリから取得した情報を元にしてクラス内のメソッド(メンバ、ファンクション)を呼びます
3.各パラメータをVARIANT型に変更し、逆順にセットします
4.クラスライブラリを呼びます
5.編集されたパラメータ、返り値をチェックします
6.エラー処理や獲得したメモリなどの解放

プログラムソース サンプル call_vbdll.cpp
コンパイル&リンクはコマンドプロンプトで
cl  call_vbdll.cpp  /link ole32.lib oleaut32.lib

プログラムソース( call_vbdll.cpp )

#include        <stdio.h>
#include        <stdlib.h>
#include        <time.h>
#include        <windows.h>

#include        <objbase.h>                     //      COM : VB.NET のDLLと接続に必要なヘッダファイル

//      -------------------------------------   BSTR <-> char変換に必要
#include        <comutil.h>
#pragma comment(lib, "comsupp.lib")                     //      必要なライブラリ
#pragma comment(lib, "comsuppw.lib")


#define         VB_DLL                          //      VB.NETの場合

//      警告 1 warning C4996: 'strcpy' が古い形式として宣言されました。などの warning C4996を消す
#pragma warning(disable : 4996)

//      -----------------------------------------------------------------
//      function :      main
//                              VB.NETで作成されたクラスライブラリ vb_dll.dllと接続し
//                              クラス Class1の中のメソッド vb_functionを呼ぶ
//      return  :          0 : success
//                       <> 0 : error
//      -----------------------------------------------------------------
void main()
{
        HRESULT     h_result;
        CLSID         clsid;
        BSTR          bstr_dll;
        char            *strUserID = "UserIDxxx";              //      ユーザーID    参照渡し
        char            *strPassNum = "PassNumyyy";       //      パスワード     参照渡し
        char            strBuf[128];                                  //      セットするメッセージなど   参照渡し
        char            buf[128];
        int               ret = 0;

        BSTR    bstr1 = _com_util::ConvertStringToBSTR( strUserID );            //      ユーザーID
        BSTR    bstr2 = _com_util::ConvertStringToBSTR( strPassNum );         //      パスワード
        long    param3 = 999;                                                                   //      処理番号
        BSTR    bstr4 = _com_util::ConvertStringToBSTR( strBuf );                 //      セットされる内容

        // COMをイニシャライズ(初期化)
        CoInitialize( NULL );

        // ProgIDからCLSIDを取得する    レジストリ HKEY_CLASSES_ROOT\に登録された DLL( ActiveX )から取得
#ifdef VB_DLL
        bstr_dll = _com_util::ConvertStringToBSTR( "vb_dll.Class1" );
#else
        bstr_dll = _com_util::ConvertStringToBSTR( "cs_dll.Class1" );
#endif
        h_result = CLSIDFromProgID( bstr_dll, &clsid );

        if ( SUCCEEDED( h_result ))
        {
                // ------------------------------------ インスタンス作成 : TestDLL.Class1
                IUnknown        *pUnk = NULL;
                h_result = CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnk );
                if ( SUCCEEDED( h_result ))
                {
                        // -------------------------------- インターフェイスを取得 : IDispatch
                        IDispatch       *pDisp = NULL;
                        h_result = pUnk->QueryInterface( IID_IDispatch, (void**)&pDisp );
                        if ( SUCCEEDED( h_result ))
                        {
                                // ---------------------------- DISPIDを取得 : 呼び出したいVB側のメンバ(メソッド): vb_function
                                DISPID          dispid = 0;
#ifdef VB_DLL
                                OLECHAR         *func_name[] = {L"vb_function"};
#else
                                OLECHAR         *func_name[] = {L"cs_function"};
#endif
                                h_result = pDisp->GetIDsOfNames( IID_NULL, func_name, 1, LOCALE_SYSTEM_DEFAULT, &dispid );
                                if ( SUCCEEDED( h_result ))
                                {
                                        // ------------------------ vb_dllに渡すパラメータ設定
                                        DISPPARAMS params;
                                        ::memset( &params, 0, sizeof( DISPPARAMS ));

                                        params.cNamedArgs = 0;
                                        params.rgdispidNamedArgs = NULL;
                                        params.cArgs = 4;                                               //      パラメータは4つ
                                        VARIANTARG* pVarg = new VARIANTARG[params.cArgs];

                                        //      ----------------------- ★パラメータは逆順に渡ることに注意!
                                        //      参照渡しで無い場合、VT_BYREFは必要ない
                                        // VT_I2 : short, VT_I4 : long, VT_BSTR : BSTR
                                        pVarg[0].vt = VT_BYREF | VT_BSTR;
                                        pVarg[0].pbstrVal = &bstr4;                             //      第1パラメータには最後を4番目をセット
                                        pVarg[1].vt = VT_BYREF | VT_I4;
                                        pVarg[1].plVal = &param3;
                                        pVarg[2].vt = VT_BYREF | VT_BSTR;
                                        pVarg[2].pbstrVal = &bstr2;
                                        pVarg[3].vt = VT_BYREF | VT_BSTR;
                                        pVarg[3].pbstrVal = &bstr1;

                                        params.rgvarg = pVarg;
                                        //      return値用
                                        VARIANT         vRet;
                                        VariantInit( &vRet );

                                        //      ******************************************************  VB.NET DLLを COMで呼ぶ
                                        pDisp->Invoke( dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, &vRet, NULL, NULL );
                                        //      ******************************************************
                                        ret = vRet.lVal;
                                        if ( ret == 0 )                 //      vb_dll, cs_dllからのリターン値をチェック
                                        {
                                                char    *psz1;
                                                char    *psz2;
                                                char    *psz4;

                                                psz1 = _com_util::ConvertBSTRToString((BSTR)bstr1);
                                                psz2 = _com_util::ConvertBSTRToString((BSTR)bstr2);
                                                psz4 = _com_util::ConvertBSTRToString((BSTR)bstr4);

                                                char    *psz;
                                                psz = _com_util::ConvertBSTRToString((BSTR)bstr1);
                                                printf( "pVarg->bstrVal 1 : %s\n", psz );
                                                psz = _com_util::ConvertBSTRToString((BSTR)bstr2);
                                                printf( "pVarg->bstrVal 2 : %s\n", psz );
                                                printf( "pVarg->bstrVal 3 : %d\n", param3 );
                                                psz = _com_util::ConvertBSTRToString((BSTR)bstr4);
                                                printf( "pVarg->bstrVal 4 : %s\n", psz );
                                                delete[] psz;
                                        }
                                        else
                                        {
                                                sprintf( buf, "VB, CS DLL からの返り値 %d\n", ret );
                                                ret = -5;
                                        }
                                        delete[] pVarg;
                                }
                                else
                                {
                                        ret = -4;
                                }
                                pDisp->Release();
                        }
                        else
                        {
                                ret = -3;
                        }
                        pUnk->Release();
                }
                else
                {
                        ret = -2;
                }
        }
        else
        {
                ret = -1;
        }
        // COMをクローズ
        CoUninitialize();

        //      ------------------------        BSTRメモリを解放
        SysFreeString( bstr1  );
        SysFreeString( bstr2  );
        SysFreeString( bstr4  );
}

コマンドプロンプトで実行して確認しました

注意すべき点は、
・呼ばれるDLLがレジストリに登録されていること
・DLL名とクラス名、そしてメソッド名が一致していること
・パラメータは逆順にセットしていること
・日本語は BSTR <--> Stringで正しく変換していること
です
========================================