从C++传递字符串到C#

3
我正在使用PInvoke、反向PInvoke方案,如Thottam R. Sriram所描述的那样。http://blogs.msdn.com/b/thottams/archive/2007/06/02/pinvoke-reverse-pinvoke-and-stdcall-cdecl.aspx 一切似乎都很顺利,除了从C++传递字符串到C#的问题。
(在Sriram中,字符串是在C#中构建并未经修改地通过C++传递的,因此避免了这个问题。)
C#代码如下:
class Program
{
    public delegate void callback(string str);
    public static void callee(string str)
    {
        System.Console.WriteLine("Managed: " + str);
    }

    static void Main(string[] args)
    {
        gpscom_start(new callback(Program.callee));

        Console.WriteLine("NMEA COM Monitor is running");
        System.Threading.Thread.Sleep(50000);
    }

    [DllImport("gpscomdll.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void gpscom_start(callback call);

}

这段 C++ 代码看起来像这样:

extern "C" dllapi void __stdcall gpscom_start(void (__stdcall *callback) (BSTR str))
{

BSTR bstr = SysAllocString(L"starting monitor");
(*callback)(bstr);
SysFreeString(bstr);

运行时,一切看起来都很好,除了回调字符串

Managed: m

看起来是由ANSI例程打印出的UNICODE字符串,但C#字符串肯定是Unicode的吧?

谢谢!

2个回答

4
当在P/Invoke边界上传递字符串时,使用适当的字符串类型和MarshalAs属性始终是一种良好的实践。我认为在参数上放置[MarshalAs(UnmanagedType.BStr)]应该可以解决问题。
public delegate void callback([MarshalAs(UnmanagedType.BStr)]string str);

这篇文章有一个类似的例子,其中有人在托管代码和非托管代码之间传递了BSTR,但他使用了IntPtr和一些Marshal类的方法。Marshal.PtrToStringBSTR在这里似乎最有用。


你能否详细说明一下?我的C#水平不够专业,不知道如何实现你的答案。 - ravenspoint
@ravenspoint 尝试将委托更改为:public delegate void callback([MarshalAs(UnmanagedType.BStr)] string str); 如果需要,导入System.Runtime.InteropServices命名空间。 这告诉运行时未管理的代码希望这是BSTR(或者可能是BSTR *,我不是interop专家) - Gideon Engelberth

1

C#字符串是UTF-16编码的。我建议C#可能期望的是LPWSTR或类似的类型,而不是BSTR。如果你看一下第一个例子,他将调用类型设置为wchar_t*,而不是BSTR。


是的,我相信BSTR也是。 - ravenspoint
BSTR不是普通的wchar_t*,它们是一种不同的二进制格式。这就是为什么需要SysAllocString的原因。 - Puppy
是的。它们都以字符串长度为前缀。据我所知,BSTR(c++) == string(c#)。 - ravenspoint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接