「C++のモジュールからC#のDLLを呼び出してみる」の版間の差分
		
		
		
		
		
		
		ナビゲーションに移動
		検索に移動
		
				
		
		
		
		
		
		
		
		
	
Rin-scrooge (トーク | 投稿記録)  (→C♯のDLL)  | 
				Rin-scrooge (トーク | 投稿記録)   | 
				||
| 64行目: | 64行目: | ||
「初期処理」「メイン処理」「後処理」で分けたので、DLLのときも応用できると思うよ。  | 「初期処理」「メイン処理」「後処理」で分けたので、DLLのときも応用できると思うよ。  | ||
| − | <  | + | <syntaxhighlight lang="cpp">  | 
// CppConsoleToCSharpDLL.cpp : コンソール アプリケーションのエントリ ポイントを定義します。  | // CppConsoleToCSharpDLL.cpp : コンソール アプリケーションのエントリ ポイントを定義します。  | ||
//  | //  | ||
| 188行目: | 188行目: | ||
	return vRet.lVal;  | 	return vRet.lVal;  | ||
}  | }  | ||
| − | </  | + | </syntaxhighlight>  | 
| + | |||
== C♯の型とC++の型とVARTYPEの関係 ==  | == C♯の型とC++の型とVARTYPEの関係 ==  | ||
戻り値を取得するのに「VARIANT型」を使用する必要があるんだ。そうなると、C♯とC++とVARIANT型の関係が分からないと、戻り値が取得できなくなっちゃうんだよ。(引数を渡すときも型の関係が分からないとだね…)ネットで調べても、なかなかまとまったものが出てこなかったので、ここにまとめておくよ。  | 戻り値を取得するのに「VARIANT型」を使用する必要があるんだ。そうなると、C♯とC++とVARIANT型の関係が分からないと、戻り値が取得できなくなっちゃうんだよ。(引数を渡すときも型の関係が分からないとだね…)ネットで調べても、なかなかまとまったものが出てこなかったので、ここにまとめておくよ。  | ||
2019年7月26日 (金) 16:47時点における最新版
こんなことほとんどする事ないんだろうけど…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#
もっと具体的な参考にしたサイトがあったんだけど…閉鎖されたか知らないところに移動したみたい…。

