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;
}

但这会将 MyClass* 返回到本机代码,而不是该 C++ 代码所期望的 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# 回调时,会在从该 C# 回调返回时调用函数 MarshalManagedToNative,这可以将 C# 引用(到 MyClassWrapper)转换为指向 C++ 类 MyClass 的指针。

这似乎足够了,然后 P/Invoke 将负责将此 MyClass* 取消引用到 MyClass 值。

并没有我想象的那么难……

于 2013-02-11T18:22:19.783 回答