将字符串从非托管代码传递给托管

问题描述:

我在将字符串从非托管代码传递到托管时遇到问题. 在我的非托管类( unmanagedClass.cpp )中,我有一个指向托管代码中函数的指针:

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接受一个字符串,但不返回任何内容:

TESTCALLBACK_FUNCTION takes one string and returns nothing:

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

非托管类继承自ITest接口,该接口只有一种方法:

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

    STDMETHOD(put_TestCallBack) (THIS_
                  LPVOID FnAddress       
             ) PURE;

在managedClass.cs中,我编写以下代码:

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

}

要从非托管代码中调用Test func,我可以使用

To call Test func from unmanaged code I use this

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

但是当调用来自ManagedClass.cs的Test方法时,我总是收到 null 字符串. 为什么会发生?

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

提前谢谢!

您在调用约定上不匹配. C ++代码中的typedef声明具有默认调用约定__cdecl的函数指针.但是托管代码中的委托的默认值为__stdcall.

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

在函数声明中放置[MarshalAs].如果可以的话,最好在C ++代码中修复typedef,显然,使所有内容保持一致是首选的解决方案:

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

您创建的委托对象对垃圾收集器不可见.如果将在下一个GC上收集,则当本机代码进行回调时,您的代码将崩溃.您必须将委托对象存储在某个地方,以便GC始终可以看到引用.要么作为类中的字段,还要求类对象必须保持足够长的生存期,要么作为静态变量.

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.

请注意,当您声明回调接口而不是委托时,所有这些问题如何消失. COM方式.

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