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#
もっと具体的な参考にしたサイトがあったんだけど…閉鎖されたか知らないところに移動したみたい…。