2010-12-15 58 views
3

,反向PInvoke的方案通过Thottam R.斯利拉姆描述http://blogs.msdn.com/b/thottams/archive/2007/06/02/pinvoke-reverse-pinvoke-and-stdcall-cdecl.aspx从C++传递字符串我使用的PInvoke C#

一切似乎很好地工作,除了从C++传递字符串到C.

(在斯利拉姆字符串在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 

它看起来像一个Unicode字符串由ANSI常规被打印出来好,但肯定C#字符串Unicode的?

TIA

回答

4

当通过P/Invoke边界对字符串进行编组时,最好使用具有适当字符串类型的MarshalAs属性。我认为在参数上放置一个[MarshalAs(UnmanagedType.BStr)]应该照顾这个问题。

public delegate void callback([MarshalAs(UnmanagedType.BStr)]string str); 

This article有哪里有人路过托管和非托管代码之间BSTRs一个类似的例子,但他用IntPtr和Marshal类中的一些方法。 Marshal.PtrToStringBSTR在这里看起来最有用。

+0

你可以请扩展一下吗?我的c#不够专业,无法知道如何实现您的答案。 – ravenspoint 2010-12-15 22:13:49

+0

@ravenspoint尝试将委托更改为:public delegate void callback([MarshalAs(UnmanagedType.BStr)] string str);'如果需要,导入System.Runtime.InteropServices命名空间。这告诉运行时,非托管代码期望这是一个BSTR(或者BSTR *,我不是互操作专家) – 2010-12-15 22:17:09

+0

这样做!非常感谢你。 – ravenspoint 2010-12-15 22:24:54

1

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

+0

是的,我相信BSTR也是。 – ravenspoint 2010-12-15 22:00:36

+0

BSTRs不是普通的wchar_t * s,它们是不同的二进制格式。这就是SysAllocString存在的原因。 – Puppy 2010-12-15 22:01:15

+0

是的。它们以字符串长度开头。据我了解,BSTR(C++)==字符串(c#) – ravenspoint 2010-12-15 22:02:42