您有三个问题需要解决:
- 调用约定不匹配。您的C++代码使用cdecl,而您的C#代码使用stdcall。
- C++代码使用宽字符串,但C#代码使用ANSI字符串进行编组。
- 第二个参数不匹配。您的C#代码假设C++代码返回一个指向新的C字符串的指针,然后C#代码使用COM分配器释放该指针。但是您的C++代码没有这样做。
现在,让我们更详细地处理这些问题。
调用约定
这很容易解决。只需将C++代码更改为stdcall或将C#代码更改为cdecl即可。但不要两者都更改。我会更改C#代码:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl]
Unicode/ANSI字符串
我认为您想要使用Unicode字符串,因为您在C++代码中明确选择了它们。但是P/invoke默认使用ANSI字符串进行封送。您可以在DllImport
中再次更改如下:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
将字符串从C++返回给C#
您当前的C++函数声明如下:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
__out
装饰器除了记录您希望修改由
strOut
指向的缓冲区并将这些修改返回给调用者外,没有任何实际作用。
您的C#声明如下:
static extern bool func(String strIn, ref String strOut)
现在,
ref String strOut
与之不匹配。在C++中,
ref
字符串参数与此匹配:
BOOL func(LPWSTR strIn, LPWSTR *strOut)
换句话说,C#代码期望你返回一个新指针。实际上,它会通过调用
CoTaskMemFree
来释放你在
strOut
中返回的缓冲区。我相信这不是你想要的。
你原始的C++代码只能通过修改传递给它的缓冲区来将字符串返回给C#代码。代码如下:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
...
wcscpy(strOut, L"the returned string");
...
}
如果您想要这样做,那么您应该在C#中的一个StringBuilder对象中分配足够的缓冲区。
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
static extern bool func(string strIn, StringBuilder strOut)
...
StringBuilder strOutBuffer = new StringBuilder(128)
bool res = func("input string", strOutBuffer)
string strOut = StringBuilder.ToString()
如果您在 C# 代码中无法确定需要多大的缓冲区,那么最好使用 BSTR
来转换 strOut。详情请参见此答案。