私の次のプロジェクトでは、C++ の既存のコードに GUI を実装したいと考えています。私の計画は、C++ 部分を DLL でラップし、GUI を C# で実装することです。私の問題は、アンマネージド DLL からマネージド C# コードにコールバックを実装する方法がわからないことです。私は既に C# でいくつかの開発を行っていますが、マネージ コードとアンマネージ コードの間のインターフェイスは初めてです。ヒントや読書のヒント、または簡単な例を教えてもらえますか? 残念ながら、役立つものは何も見つかりませんでした。
4 に答える
Marshal.GetFunctionPointerForDelegate() を使用する必要はありません。P/Invoke マーシャラーが自動的に行います。C++ 側の関数ポインター宣言と互換性のある署名を持つデリゲートを C# 側で宣言する必要があります。例えば:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
class UnManagedInterop {
private delegate int Callback(string text);
private Callback mInstance; // Ensure it doesn't get garbage collected
public UnManagedInterop() {
mInstance = new Callback(Handler);
SetCallback(mInstance);
}
public void Test() {
TestCallback();
}
private int Handler(string text) {
// Do something...
Console.WriteLine(text);
return 42;
}
[DllImport("cpptemp1.dll")]
private static extern void SetCallback(Callback fn);
[DllImport("cpptemp1.dll")]
private static extern void TestCallback();
}
アンマネージ DLL の作成に使用される、対応する C++ コードは次のとおりです。
#include "stdafx.h"
typedef int (__stdcall * Callback)(const char* text);
Callback Handler = 0;
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int retval = Handler("hello world");
}
それはあなたがそれを始めるのに十分です。トラブルに巻き込まれる可能性のある詳細は無数にあり、そのうちのいくつかは必ず遭遇します。この種のコードを実行するためのはるかに生産的な方法は、C++/CLI 言語でラッパーを作成することです。これにより、C++ クラスをラップすることもできます。これは、P/Invoke ではできないことです。適切なチュートリアルはこちらから入手できます。
P/Invoke は、マネージ デリゲートの関数ポインターへのマーシャリングを処理できます。したがって、DLL からのコールバック関数を登録する API を公開し、C# でその関数にデリゲートを渡すとします。
EnumWindows 関数でこれを行うMSDNの例があります。その記事では、次のように述べているポイント 4 の行に注意を払うように注意してください。
ただし、呼び出しが返された後にコールバック関数を呼び出すことができる場合、マネージ呼び出し元は、コールバック関数が終了するまでデリゲートが収集されないようにするための手順を実行する必要があります。ガベージ コレクションの防止の詳細については、「プラットフォーム呼び出しによる相互運用マーシャリング」を参照してください。
つまり、マネージ コードがデリゲートへの参照をコード内に保持するかピン留めすることによって呼び出しが完了するまで、デリゲートがガベージ コレクションされないようにする必要があるということです。
Marshal.GetFunctionPointerForDelegateを参照してください。これにより、アンマネージド コードからマネージド (つまり C# コード) を呼び出すための関数ポインターが得られます。
このMarshal.GetDelegateForFunctionPointerを見てください。