C#调用C++编译的DLL时整数方法正常但字符串返回乱码如何解决?
问题根因
字符串乱码问题本质是跨语言交互的类型不兼容,核心有两个问题:
- C++ 标准库的
std::string是包含数据指针、长度、容量等成员的复合结构体,和 C# 期望的以\0结尾的C风格字符串指针完全不匹配,C# 会把结构体的内存内容当成指针解析,自然读到乱码。 std::string是C++函数内的临时对象,函数执行结束后就会被析构,对应的内存被回收,C#拿到的是野指针,所以每次运行的乱码内容都不一样。
整数返回正常是因为int是基础值类型,跨平台跨语言的内存布局一致,不存在解析和内存管理问题。
解决方案
方案一:简易适配(适合返回固定字符串场景)
1. 修改C++端代码
只需要把返回值从std::string改为C风格的const char*,返回静态存储的字符串字面值即可:
// test.h 修改 #include <iostream> using namespace std; #define EXPORT_API __declspec(dllexport) extern "C" EXPORT_API const char* SayHi(); extern "C" EXPORT_API int CallNumber();
// test.cpp 修改 #include <iostream> #include "test.h" using namespace std; const char* SayHi(){ return "hi"; // 字符串字面值存储在静态常量区,不会被释放 } int CallNumber(){ return 911; }
还是用原命令编译生成DLL:g++ -shared -o test.dll test.cpp
2. 修改C#端代码
需要在DllImport特性中指定字符集,并且告知.NET不要自动释放返回的字符串内存(这部分是C++的静态内存,不需要C#回收):
using System; using System.Runtime.InteropServices; namespace Test.Main { class Program { [DllImport(@"test.dll", CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.LPStr)] public static extern string SayHi(); [DllImport(@"test.dll")] public static extern int CallNumber(); static void Main(string[] args) { Console.WriteLine(SayHi()); Console.WriteLine(CallNumber()); Console.ReadKey(); } } }
方案二:通用安全方案(适合返回动态字符串场景)
如果返回的字符串是动态生成的(比如运行时拼接的内容),静态字符串无法满足需求,推荐用C#分配缓冲区,C++写入内容的方式,内存完全由C#管理,不存在泄漏风险:
1. 修改C++端代码
// test.h 修改 extern "C" EXPORT_API void SayHi(char* buffer, int bufferLen);
// test.cpp 修改 void SayHi(char* buffer, int bufferLen){ const char* res = "hi"; strncpy(buffer, res, bufferLen - 1); // 留一个位置给结束符\0 buffer[bufferLen - 1] = '\0'; }
2. 修改C#端代码
[DllImport(@"test.dll", CharSet = CharSet.Ansi)] public static extern void SayHi(StringBuilder buffer, int bufferLen); // 调用时先分配足够大的缓冲区 static void Main(string[] args) { StringBuilder sb = new StringBuilder(256); // 分配256字节的缓冲区 SayHi(sb, sb.Capacity); Console.WriteLine(sb.ToString()); Console.WriteLine(CallNumber()); Console.ReadKey(); }
注意事项
- 不推荐在C++中用
new或者malloc分配字符串内存返回给C#,如果必须这么做,要用CoTaskMemAlloc分配内存,C#默认会用CoTaskMemFree释放返回的字符串内存,否则会出现内存泄漏。 - 如果需要返回中文等非ASCII字符,建议统一用UTF8编码,两边指定对应的字符集即可。
内容的提问来源于stack exchange,提问作者Rizquuula
相关产品推荐
相关产品推荐

