C++dll返回的字符串在C#调用程序中损坏,原因是什么
本文关键字:损坏 是什么 程序 调用 返回 字符串 C++dll | 更新日期: 2023-09-27 17:59:14
我有一个C#应用程序,它调用C++DLL。
在C#中,我有如下代码:
[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
public extern static String Func1(String arg);
......
String arg = "test text";
String retstring = Func1(arg);
在CPP中,我的功能定义如下:
extern "C"
{
__declspec(dllexport) LPWSTR Func1(LPWSTR arg)
{
....
LPWSTR ret1 = L"1?2?3?4?5";
LPWSTR ret2 = SomeActualFunction(arg);
retturn ret1; // return ret2;
}
}
如果我在C++的Func1()中返回ret1,一切都很好。在VS2008的内存窗口中,我可以看到正确的Unicode二进制文件。在C++中,ret1的二进制文件是
,而在C#中,retstring的二进制文件是
我认为C#二进制
"28 67 a3 f7 fe 07 00 00 0a 00 00 09 00 00 00"
是System.String类型的标头。
更重要的是,如果我在CPP代码中返回之前添加以下行,我也可以在C#代码中获得正确的retstring:
ret2 = L"1?2?3?4?5";
但是当我在C++DLL中返回ret2时,C#中返回的字符串ret似乎已损坏。经过我的检查,C++DLL中的二进制文件是正确的Unicode。但是C#代码中retstring的二进制文件是
"28 67 a3 f7 fe 07 00 00 0 a 00 00 09 00 00 00 dd dd dd dd dddd dd dd…"。
我只能注意到ret2比ret1长——ret2有数百个WCHAR。
有什么想法吗?提前Thx。
我总是使用BSTR
,因为它使内存分配/释放的责任透明。
C++
#include <comutil.h>
BSTR GetSomeText()
{
return ::SysAllocString(L"Greetings from the native world!");
}
C#
[DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();
您没有说明如何分配字符串,但一旦使用了动态分配的字符串,就需要解决这个问题。BSTR
的伟大之处在于它使用了共享的COM分配器,使C#编组器能够使用与分配字符串的C++代码相同的分配器来释放字符串。
[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPStr)]
public extern static String Func1(String arg);
或
[DllImport(@"111.dll", CharSet = CharSet.Unicode)]
public extern static IntPtr Func1(String arg);
// In your calling code
string result = Marshal.PtrToStringUni(Func1("somestring"));
您的函数是__cdecl还是__stdcall调用约定?IIRC,C#的默认值为__stdcall
,但C++的默认值是__cdecl
。尝试添加CallingConvention=CallingConvention.Cdecl
。
另一种可能性是:既然你说返回静态字符串有效,那么SomeActualFunction
返回的指针仍然有效吗?如果它指向该函数中的本地缓冲区,则从该函数返回后它将不再有效。
因此,如果将指针返回到硬编码字符串L"1?2?3?4?5",则一切正常。但如果返回SomeActualFunction,则答案不正确。也许C++代码不正确?SomeActualFunction是如何工作的?例如,它可能会从某个堆栈分配的对象返回LPWSTR,该对象在此时被销毁。试着先用C++客户端测试一下。
我已经在SkyDrive上上传了一个示例代码(http://cid-48a119f5ed65483e.office.live.com/self.aspx/.Public/MarshalString.zip)。它演示了如何将托管字符串传递给非托管代码以及如何操作它
我想我做到了:对C++DLL的C#调用,向C#返回一个字符串。我将分享我认为使其发挥作用的因素:
在C#中
using System.Runtime.InteropServices;
[DllImport(
@"C:'Users'Ron'Documents'Visual Studio 2013' etc. ...(The whole path)... my.dll,
CallingConvention = CallingConvention.Cdecl
)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string cpp_brand_files(string home_dir, string xml_lic);
以及稍后在C#中的调用:
string a_str = cpp_brand_files(home_dir, xml_license);
在C++DLL中:
#using <mscorlib.dll>
using namespace System;
extern "C" __declspec(dllexport)
wchar_t* cpp_brand_files(char* home_dir, char* xml_lic);
在C++中更进一步:
wchar_t* cpp_brand_files(char* home_dir, char* xml_lic)
{
BSTR samp = ::SysAllocString( L"This is the long, long string." );
return samp;
}
此外,在配置属性中使用/cli(公共语言运行时支持)编译C++DLL。右键单击C++项目,然后单击属性->配置属性->常规->公共语言运行时支持。
所以我认为这就是它工作的基本原理(返回字符串)。
我正在使用Visual Studio 2013 RC。专家们,请评论哪些是多余的或缺少的。
如果需要封送其他数据类型,这里有一个链接供您参考更新了.NET 4.0的链接https://msdn.microsoft.com/en-us/library/sak564ww(v=vs.100).aspx