Question

I have a problems with passing string from unmanaged code to managed. In my unmanaged class (unmanagedClass.cpp) I have a pointer to function from managed code:

TESTCALLBACK_FUNCTION testCbFunc;

TESTCALLBACK_FUNCTION takes one string and returns nothing:

typedef void (*TESTCALLBACK_FUNCTION )(char* msg);

Unmanaged class inherites from ITest interface which has only one method:

    STDMETHOD(put_TestCallBack) (THIS_
                  LPVOID FnAddress       
             ) PURE;

In managedClass.cs I write this code:

public class ManagedClass
{
    ITest unmanaged = new unmanagedClass();
    public delegate void TestDelegate(string info);
    ManagedClass()
    {
        unmanaged.put_TestCallBack(new TestDelegate(this.Test));
    }
    void Test(string info)
    {
            MessageBox.Show(info);
    }
}

[ComImport, Guid("<my guid here>")]
public class unmanagedClass
{
}

[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
Guid("<my guid here>"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITest
{
    [PreserveSig]
    int put_TestCallBack([MarshalAs(UnmanagedType.FunctionPtr), In] Capture.TestDelegate func);

}

To call Test func from unmanaged code I use this

(*testCbFunc)("Func Uragan33::Execute has been started!");

But when Test method from managedClass.cs is called I always received null string. Why does it happen?

Thank in advance!

Was it helpful?

Solution

You have a mismatch on the calling convention. The typedef in your C++ code declares a function pointer with the default calling convention, which is __cdecl. But the default for a delegate in managed code is __stdcall.

You will need an attribute to tell the pinvoke marshaller otherwise. Make that look like this:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void TestDelegate(string info);

Drop the [MarshalAs] in the function declaration. Fixing the typedef in your C++ code might be preferable, if you can, clearly making everything consistent is the preferred solution:

    typedef void (__stdcall * TESTCALLBACK_FUNCTION )(char* msg);

Unrelated, this a bug you'll need to fix:

   unmanaged.put_TestCallBack(new TestDelegate(this.Test));

The delegate object you create is not visible to the garbage collector. If will be collected on the next GC, your code will crash when the native code makes the callback. You have to store the delegate object somewhere so the GC always sees a reference. Either as a field in the class, with the additional requirement that the class object needs to stay alive long enough, or in a static variable.

Note how all of these problems disappear when you declare a callback interface instead of a delegate. The COM way.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top