2

I have this code in C++

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

I am using swig but for simplicity (if you don't know swig) I have this wrapper in 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++
   }
};

Now I want to support the callback mechanism in C# so I set it up like this:

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

I have all this code setup to work properly. The native code in CallCallback will call MyFunction properly.

But now I need to handle the returned object properly...

MyFunction returns a C# reference while the callback in C++ is returning by value so this would not work for free:

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

How can I marshall the "reference" to a MyClassWrapper object, returned from MyFunction, so that C++ receives "by-value" a MyClass object ?

Should I go ahead and write a custom marshaller ?

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

Then use it like here

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

I looked at the documentation for custom marshallers and it's quite complex. Looks like the interesting method to implement is the following:

IntPtr MarshalManagedToNative( Object ManagedObj );

And the code will be something like

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

But this will return a MyClass* back to the native code, not a MyClass value that this C++ code expects !

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

Will the marshaller be smart enough to dereference the pointer ?

Éric
  • 181
  • 2
  • 15
  • Is there a compelling reason why you can't have the callback return a pointer, and dereference it on the C++ side? – cdhowie Feb 08 '13 at 22:49
  • I have no control on the C++ code unfortunately... – Éric Feb 08 '13 at 23:07
  • Diferent managed signiture can be wrrapped using anonymous medhod that will do the marrshaling. As for return by value you could declare an struct with fixed size byte array inside and use that in unmanaged callback signiture. Then in your ananimus method simply use Marshall.Copy to fill that array from pointer stored in wrapper. You will need to know the size of your native object(sizeof ?). – user629926 Feb 09 '13 at 06:02
  • 1
    You cannot instantiate a C++ class in your C# code. You need native code for that. C++/CLI looks good here. – David Heffernan Feb 09 '13 at 12:35
  • user629926: I would like to try your suggestion. I understand your suggestion about using a signature with fixed size byte array but I don't understand how to use anonymous methods here to "insert" this conversion. – Éric Feb 11 '13 at 16:09

1 Answers1

1

Thank you all for your comments

Looks like the custom marshaler is the way to go !

I did a simple test case and everything works fine. It works as expected. Here is the marshaler in case you are interested:

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();

With this marshaler when C++ calls the C# callback the function MarshalManagedToNative is called on return from that C# callback and this enables to convert the C# reference (to MyClassWrapper) to a pointer to the C++ class MyClass.

This seems sufficient enough and P/Invoke will then take care of dereferencing this MyClass* to a MyClass value.

It wasn't as hard as I thought...

Éric
  • 181
  • 2
  • 15