C#调用带C包装层的C++ DLL传递字符串乱码问题求助
解决C#调用C++ DLL时字符串传递的乱码问题
这个乱码问题我之前也碰到过,核心原因是C#与C/C++之间的字符串编码不匹配——C#的string默认是UTF-16(双字节Unicode),但你的C/C++代码用的是char*(单字节ANSI编码),跨边界传递时没有做正确的编码转换,导致内存解析错误,就出现了那些乱码字符。下面给你几个可行的解决方案:
方案一:修改C#端的编码匹配(最快解决)
直接在DllImport中指定字符集为ANSI,让C#自动把UTF-16字符串转换成ANSI编码的char*传递给DLL,完美匹配你的现有C/C++代码:
[DllImport(DllName, CallingConvention = DllCallingConvention, CharSet = CharSet.Ansi)] public static extern IntPtr AllocateHandle(string filename);
这样修改后,C#会自动处理编码转换,你的C++构造函数里直接把char*赋值给std::string就不会有乱码了。
方案二:改用Unicode宽字符(推荐,支持多语言)
如果你的程序需要支持中文、日文等非ASCII字符,推荐改用宽字符方案,从根本上避免编码问题:
- 修改C包装层代码:把
char*换成wchar_t*
LPVOID SAMPLEDLL_API CALLCONV_API AllocateHandle(wchar_t* filename);
- 修改C++类构造函数:用
std::wstring存储宽字符
#include <string> CustomData::CustomData(wchar_t* filename) { _filename = std::wstring(filename); // 改为std::wstring类型存储 }
- 修改C#端的DllImport:指定Unicode字符集(其实C#默认就是Unicode,明确写出来更清晰)
[DllImport(DllName, CallingConvention = DllCallingConvention, CharSet = CharSet.Unicode)] public static extern IntPtr AllocateHandle(string filename);
如果之后需要把宽字符串转成窄字符串输出到文件,可以用C++11的std::wstring_convert,或者Windows API的WideCharToMultiByte来做转换。
方案三:手动在C#端转换编码(不修改DLL时用)
如果暂时不想改动DLL代码,可以在C#端手动把字符串转成ANSI字节数组,再传递指针给DLL:
public static IntPtr AllocateHandle(string filename) { // 把字符串转成ANSI编码的字节数组,必须加上'\0'终止符 byte[] ansiBytes = System.Text.Encoding.Default.GetBytes(filename + '\0'); // 分配非托管内存 IntPtr ptr = Marshal.AllocHGlobal(ansiBytes.Length); Marshal.Copy(ansiBytes, 0, ptr, ansiBytes.Length); try { // 调用原生DLL函数 return NativeAllocateHandle(ptr); } finally { // 必须释放非托管内存,避免内存泄漏 Marshal.FreeHGlobal(ptr); } } // 内部调用的原生函数,参数为IntPtr [DllImport(DllName, CallingConvention = DllCallingConvention)] private static extern IntPtr NativeAllocateHandle(IntPtr filename);
额外注意事项
- 不管用哪种方案,都要确保传递的字符串是以
\0(空字符)终止的——C/C++的char*/wchar_t*依赖空终止符来识别字符串长度。 - 保存
_filename到文件时,要匹配存储的字符串类型:比如用std::string就用普通的fopen/printf输出;用std::wstring则需要用_wfopen/fwprintf这类宽字符输出函数。
内容的提问来源于stack exchange,提问作者Mathieu Gauquelin
相关产品推荐
相关产品推荐

