亀岡的プログラマ日記

京都のベッドタウン、亀岡よりだらだらとお送りいたします。

C#のdelegateをCLI経由でC++の関数ポインタに渡す

え?そんなのMarshal.GetFunctionPointerForDelegate使えば一瞬じゃないの?と思いますよね。
意外と大変なんです。なんというかCLIをかませるほうがなんだか面倒くさくなっています。関数ポインタへのキャストがなん弾か必要なのと、__stdcallを関数ポインタに付与する必要があるのに、だいぶハマりました。

まぁ、とりあえずサンプル作った時点で睡魔が限界なので、とりあえずそれだけでも。
(ちなみにクラス名が謎いのは昔のWritableBitmapの記事のサンプルを流用してるからです。)

C#
    //関数ポインタに引き渡すデリゲート
    delegate int FunctionPointerTarget(int a,int b);
    var wrapper = new WritableBitmapCalcWrapper();

    FunctionPointerTarget fp = (a, b) => a + b;

    var functionPointer = Marshal.GetFunctionPointerForDelegate(fp);
    //関数ポインタが指しているデリゲートがGCに回収されないようメモリを確保する
    _gcHandle = GCHandle.Alloc(fp);

    //voidポインタで渡す場合はunsafeが必要。別にCLIでやってもいいので趣味の問題。
    unsafe
    {
        wrapper.SetFunction(functionPointer.ToPointer());
    }
    //セットした関数をコール
    var x = wrapper.CallFunction(4, 8); 
    //x = 12;
C++/CLI
	//関数をセット。void*から関数ポインタの方へ変換する。
	void SetFunction(void* fp){
		calc->SetFunction(static_cast<FunctionPointer>(fp));
	}
	//セットした関数を呼ぶ
	int CallFunction(int a, int b){
		return calc->CallFunction(a,b);
C++
//関数ポインタの宣言。__stdcallをの付与は必須
typedef int (__stdcall *FunctionPointer)(int,int);
private:
	FunctionPointer _fp;
	void SetFunction(FunctionPointer fp){
		_fp = fp;
	}
public:
	int CallFunction(int a, int b){
		return _fp(a,b);
	}