2

私はC++でこのコードを持っています

class MyClass { ... };
typedef MyClass (*Callback)();
Callback theCB;
static void RegisterCallback( Callback cb ) { theCB = cb; };
static void CallCallback() { 
   MyClass obj = theCB();
}

私はswigを使用していますが、簡単にするために(swigがわからない場合)、C#でこのラッパーを使用しています

public class MyClassWrapper
{
   public IntPtr ptrToNativeObj; // pointer to native MyClass object
   public MyClassWrapper()
   {
       ptrToNativeObj = call to native code that creates 
                        and returns a new instance of MyClass in C++
   }
};

ここで、C# でコールバック メカニズムをサポートしたいので、次のように設定します。

public MyClassWrapper MyFunction()
{
   return new MyClassWrapper();
}

delegate MyClassWrapper CallbackDotNet();

static void main()
{
    var fct = new CallbackDotNet( MyFunction );
    P/Invoke call to native function RegisterCallback( fct );

    then finally:
    P/Invoke call to native function CallCallback();
}

このコードはすべて正しく動作するように設定されています。CallCallback のネイティブ コードは MyFunction を適切に呼び出します。

しかし、返されたオブジェクトを適切に処理する必要があります...

MyFunction は、C++ のコールバックが値によって返されている間に C# 参照を返すため、これは無料では機能しません。

static void CallCallback() { 
   MyClass obj = theCB();
}

MyFunction から返された MyClassWrapper オブジェクトへの「参照」をマーシャリングして、C++ が MyClass オブジェクトを「値渡し」で受け取るようにするにはどうすればよいですか?

先に進んでカスタム マーシャラーを作成する必要がありますか?

http://msdn.microsoft.com/en-us/library/zk0a8dea(v=vs.90).aspx

次に、ここのように使用します

[return: MarshalAs(UnmanagedType.CustomMarshaler,
         MarshalType = "MyCustomMarshaler")] 
delegate MyClassWrapper CallbackDotNet();

カスタム マーシャラーのドキュメントを見たところ、非常に複雑です。実装する興味深い方法は次のとおりです。

IntPtr MarshalManagedToNative( Object ManagedObj );

そして、コードは次のようになります

IntPtr MarshalManagedToNative( Object ManagedObj )
{
   MyClassWrapper val = ManagedObj as MyClassWrapper;
   return val.ptrToNativeObj;
}

しかし、これは、この C++ コードが期待する MyClass 値ではなく、MyClass* をネイティブ コードに返します。

static void CallCallback() { 
   MyClass obj = theCB();
}

マーシャラーは、ポインターを逆参照するのに十分賢いでしょうか?

4

1 に答える 1

1

コメントありがとうございます

カスタム マーシャラーが最適なようです。

簡単なテストケースを実行しましたが、すべて正常に動作します。期待どおりに動作します。興味がある場合のマーシャラーは次のとおりです。

public class MyCustomMarshaler : ICustomMarshaler
{
    public static ICustomMarshaler GetInstance(String cookie)
    {
        return new MyCustomMarshaler();
    }
    public IntPtr MarshalManagedToNative(Object ManagedObj)
    {
        MyClassWrapper val = ManagedObj as MyClassWrapper;
        return val.ptrToNativeObj;
    }
    ...
 }

 [return: MarshalAs(UnmanagedType.CustomMarshaler,MarshalType = "MyCustomMarshaler")]
 public delegate MyClassWrapper CallbackDotNet();

このマーシャラーを使用すると、C++ が C# コールバックを呼び出すときに、関数 MarshalManagedToNative がその C# コールバックからの戻り時に呼び出され、これにより (MyClassWrapper への) C# 参照を C++ クラス MyClass へのポインターに変換できます。

これで十分と思われるので、P/Invoke はこの MyClass* を MyClass 値に逆参照します。

思ったより辛くなかった…

于 2013-02-11T18:22:19.783 に答える