C++のモジュールからC#のDLLを呼び出してみる
提供: とある社畜の頭脳整理
こんなことほとんどする事ないんだろうけど…MetaTraderの開発をしているとしたくて仕方なくなることがあるんだよ(笑)
目次
[非表示]C♯のプロジェクト作成
DLLだけにクラスライブラリでプロジェクトを作成するんだけど…プロジェクトのプロパティを変更する必要があるんだよ。
アセンブリ情報の設定
- プロジェクトのプロパティを開く
- 「アプリケーション」のタブで「アセンブリ情報」のボタンをクリックする
- 「アセンブリをCOM参照可能にする」のチェックボックスにチェックを入れる
- 「OK」ボタンをクリックする
ビルドの設定
- プロジェクトのプロパティを開く
- 「ビルド」のタブで「COM相互運用機能の登録」にチェックを入れる
- 保存する
注意事項
OSにログインしているアカウントの権限によっては、ビルド時にレジストリへの登録に失敗するエラーが発生することがあるんだ。そんなときはVisualStudioを管理者権限で実行すれば登録できるよ。
ソース
C♯のDLL
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; //追加 using System.Runtime.InteropServices; namespace CSharpDLL { /// <summary> /// クラスインターフェース設定 /// </summary> [ClassInterface(ClassInterfaceType.AutoDual)] /// <summary> /// 合計クラス /// </summary> public class CSharpDLL { /// <summary> /// 合計処理 /// </summary> /// <param name="p_Number1">数値1</param> /// <param name="p_Number2">数値2</param> /// <returns></returns> public Int32 Sum(Int32 p_Number1, Int32 p_Number2) { return p_Number1 + p_Number2; } } }
C++のソース
「初期処理」「メイン処理」「後処理」で分けたので、DLLのときも応用できると思うよ。
// CppConsoleToCSharpDLL.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" //追加 #pragma comment(lib, "comsupp.lib") #pragma comment(lib, "comsuppw.lib") #include <Windows.h> #include <comutil.h> #include <objbase.h> //グローバル変数 IDispatch *pIDisp = NULL; IUnknown *pIUnk = NULL; //プロトタイプ宣言 long _Init(void); long _Finalize(void); long _Sum(long p_Number1, long p_Number2); int _tmain(int argc, _TCHAR* argv[]) { //変数宣言 int l_Result = 0; //初期処理 _Init(); //合計処理 l_Result = _Sum(300, 500); //後処理 _Finalize(); printf("計算結果:%d", l_Result); } //***************************************************************************// //初期化関数 //***************************************************************************// long _Init(void) { CLSID clsid; //COM初期化 ::CoInitialize(0); //ProcIDからCLSIDを取得(ネームスペース名.クラス名) HRESULT h_result = CLSIDFromProgID(L"CSharpDLL.CSharpDLL", &clsid); if (FAILED(h_result)) { return -1; } //Instanceの生成 h_result = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pIUnk); if (FAILED(h_result)) { return -2; } //インターフェースの取得(pIDispは共通変数) h_result = pIUnk->QueryInterface(IID_IDispatch, (void**)&pIDisp); if (FAILED(h_result)) { return -3; } //正常終了 return 0; } //***************************************************************************// //後始末処理 //***************************************************************************// long _Finalize(void) { pIDisp->Release(); pIUnk->Release(); ::CoUninitialize(); return 0; } //***************************************************************************// //合計処理呼出処理 //***************************************************************************// long _Sum(long p_Number1, long p_Number2) { //DISPIDの取得(関数名の設定) DISPID dispid = 0; OLECHAR *Func_Name[] = { L"Sum" }; HRESULT h_result = pIDisp->GetIDsOfNames(IID_NULL, Func_Name, 1, LOCALE_SYSTEM_DEFAULT, &dispid); if (FAILED(h_result)) { return -1; } //パラメータ作成 DISPPARAMS params; ::memset(¶ms, 0, sizeof(DISPPARAMS)); params.cNamedArgs = 0; params.rgdispidNamedArgs = NULL; params.cArgs = 2; //呼び出す関数の引数の数 //引数設定(順番に注意…逆になる) VARIANTARG* pVarg = new VARIANTARG[params.cArgs]; pVarg[0].vt = VT_I4; pVarg[0].lVal = p_Number2; pVarg[1].vt = VT_I4; pVarg[1].lVal = p_Number1; params.rgvarg = pVarg; VARIANT vRet; VariantInit(&vRet); //呼び出し pIDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &vRet, NULL, NULL); delete[] pVarg; return vRet.lVal; }
C♯の型とC++の型とVARTYPEの関係
戻り値を取得するのに「VARIANT型」を使用する必要があるんだ。そうなると、C♯とC++とVARIANT型の関係が分からないと、戻り値が取得できなくなっちゃうんだよ。(引数を渡すときも型の関係が分からないとだね…)ネットで調べても、なかなかまとまったものが出てこなかったので、ここにまとめておくよ。
C♯ | C++ | VARTYPE | 使用するメンバ |
---|---|---|---|
short (System.Int16) | SHORT (short) | VT_I2 | iVal |
int (System.Int32) | INT (int) LONG (long) |
VT_I4 | lVal |
bool (System.Boolean) | BOOL (long) | VT_BOOL | boolVal |
string (System.String) | LPCSTR (const char *) LPCWSTR (const wchar_t *) |
VT_BSTR | bstrVal |
double (System.Double) | DOUBLE (double) | VT_R8 | dblVal |
float (System.Single) | FLOAT (float) | VT_R4 | fltVal |
参考サイト
動的な実行
@IT:.NET TIPS Win32 APIやDLL関数を呼び出すには? - C#
もっと具体的な参考にしたサイトがあったんだけど…閉鎖されたか知らないところに移動したみたい…。