我可以看到你有三个问题。
- 调用约定不匹配。您的 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 根本不匹配。 ref 字符串参数在 C++ 中与此匹配:
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。有关详细信息,请参阅此answer。