メインメニューを開く

差分

C++のモジュールからC#のDLLを呼び出してみる

6,616 バイト追加, 2019年7月22日 (月) 13:27
ページの作成:「こんなことほとんどする事ないんだろうけど…MetaTraderの開発をしているとしたくて仕方なくなることがあるんだよ(笑) ==…」
こんなことほとんどする事ないんだろうけど…MetaTraderの開発をしているとしたくて仕方なくなることがあるんだよ(笑)

== C♯のプロジェクト作成 ==
DLLだけにクラスライブラリでプロジェクトを作成するんだけど…プロジェクトのプロパティを変更する必要があるんだよ。
=== アセンブリ情報の設定 ===
<ol>
<li>プロジェクトのプロパティを開く</li>
<li>「アプリケーション」のタブで「アセンブリ情報」のボタンをクリックする</li>
<li>「アセンブリをCOM参照可能にする」のチェックボックスにチェックを入れる</li>
<li>「OK」ボタンをクリックする</li>
</ol>
[[ファイル:CppConsoleToCSharpDll-001.jpg]]

=== ビルドの設定 ===
<ol>
<li>プロジェクトのプロパティを開く</li>
<li>「ビルド」のタブで「COM相互運用機能の登録」にチェックを入れる</li>
<li>保存する</li>
</ol>
[[ファイル:CppConsoleToCSharpDll-002.jpg]]

=== 注意事項 ===
OSにログインしているアカウントの権限によっては、ビルド時にレジストリへの登録に失敗するエラーが発生することがあるんだ。そんなときはVisualStudioを管理者権限で実行すれば登録できるよ。

== ソース ==
=== C♯のDLL ===
<source lang="csharp">
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;
}
}
}
</source>

=== C++のソース ===
「初期処理」「メイン処理」「後処理」で分けたので、DLLのときも応用できると思うよ。

<source lang="cpp">
// 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(&params, 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, &params, &vRet, NULL, NULL);

delete[] pVarg;
return vRet.lVal;
}
</source>
== C♯の型とC++の型とVARTYPEの関係 ==
戻り値を取得するのに「VARIANT型」を使用する必要があるんだ。そうなると、C♯とC++とVARIANT型の関係が分からないと、戻り値が取得できなくなっちゃうんだよ。(引数を渡すときも型の関係が分からないとだね…)ネットで調べても、なかなかまとまったものが出てこなかったので、ここにまとめておくよ。
{| class="wikitable"
|-
!C♯!!C++!!VARTYPE!!使用するメンバ
|-
|short (System.Int16)||SHORT (short)||VT_I2||iVal
|-
|int (System.Int32)||INT (int)<br/>LONG (long)||VT_I4||lVal
|-
|bool (System.Boolean)||BOOL (long)||VT_BOOL||boolVal
|-
|string (System.String)||LPCSTR (const char *)<br/>LPCWSTR (const wchar_t *)||VT_BSTR||bstrVal
|-
|double (System.Double)||DOUBLE (double)||VT_R8||dblVal
|-
|float (System.Single)||FLOAT (float)||VT_R4||fltVal
|}

== 参考サイト ==
[http://eternalwindows.jp/com/auto/auto01.html 動的な実行]<br/>
[http://www.atmarkit.co.jp/fdotnet/dotnettips/024w32api/w32api.html @IT:.NET TIPS Win32 APIやDLL関数を呼び出すには? - C#]<br/>
もっと具体的な参考にしたサイトがあったんだけど…閉鎖されたか知らないところに移動したみたい…。

[[Category:C♯]]
[[Category:C++]]
[[Category:dll]]