7

呼び出し元のコンテキストを識別する方法がないコールバック メカニズムを持つ (C) サード パーティ ライブラリを使用しようとしています。私のメイン プロジェクトは C# で、ラッパーは C ライブラリ API を呼び出す C++/CLI プロジェクトです。

この問題を回避するために、Marshal::GetFUnctionPointerForDelegate を使用しようとしています。ここに私のC#コードがあります:

void Init(Handler Callback)
{
    // Create a wrapper lambda in case Callback is a non-static method.
    instance.CreateBuffers((x, y) => Callback(x, y));
}

次に、C++/CLI コードで次のようにします。

void CreateBuffers(Handler^ Callback)
{
    pinCallback = GCHandle::Alloc(Callback);

    void (*callback)(int, int) = (void (__stdcall *)(int, int))Marshal::GetFunctionPointerForDelegate(Callback).ToPointer(),
    // Use 'callback' in third party library...
}

このすべての問題は、http://msdn.microsoft.com/en-us/library/367eeye0.aspx によると Marshal::GetFunctionPointerForDelegate からの関数ポインターは stdcall 関数ですが、私の C コールバックは cdecl です。ここでcdecl互換関数を取得するにはどうすればよいですか?

4

1 に答える 1

7

これを実現する最も簡単な方法は、呼び出し規約を指定する UnmanagedFunctionPointer 属性を使用して、デリゲート型が C# であることを宣言することだと思います。

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate ...

次に、C++/CLI コードがプレーンなネイティブ C++ 関数ポインターを受け取るようにします。デリゲートのインスタンスを渡すことができます。デリゲートにはその属性があるため、マーシャラーは何をすべきかを知っています。

これにより、GetFunctionPointerForDelegate ステップをスキップできます。実際には、C++/CLI レイヤーをスキップすることができますが、そうしたくないと思います。

アンマネージ コードが関数ポインターを認識できる限り、マネージ コードがデリゲートへの参照を保持していることを確認してください。そうしないと、アンマネージ コードが保持する参照をガベージ コレクターが認識できないため、ガベージ コレクターがあなたの下から敷物を引っ張る可能性があります。

もちろん、このトピックは以前にここで取り上げられています。Hans Passant には、読む価値のある詳細な回答があります: C# から C DLL メソッドを呼び出す正しい方法

于 2013-11-02T08:37:55.220 に答える