C#调用C++ DLL导出的std::string返回值函数无输出如何解决
C# P/Invoke调用C++ DLL返回字符串失败的解决方法
核心问题原因
- C++的
std::string是标准库专属自定义类型,不属于C语言兼容的原生互操作类型,C#的P/Invoke封送器无法识别其内存布局,无法正确解析返回值。 std::string的内存由C++标准库管理,直接跨语言返回会导致内存所有权混乱,轻则无法读取内容,重则触发内存泄漏、程序崩溃。
修复方案
方案1:返回C风格常量字符串(适合固定内容的返回值)
修改后C++导出代码
extern "C" { __declspec(dllexport) int SM_Interop_API_Add(int a, int b) { return a + b; } // 返回值改为C兼容的const char*,字符串常量存储在全局常量区,生命周期随进程 __declspec(dllexport) const char* SM_Interop_API_getLanguage() { return "This is Test String"; } }
修改后C#调用代码
class Program { [DllImport(@"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SM_Interop_API_Add")] public static extern int SM_Interop_API_Add(int a, int b); // 返回值改为IntPtr接收,避免C#默认尝试释放常量区内存 [DllImport(@"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SM_Interop_API_getLanguage")] public static extern IntPtr SM_Interop_API_getLanguage(); static void Main(string[] args) { Console.WriteLine("Hello World!"); int sum = SM_Interop_API_Add(10, 10); Console.WriteLine($"Result: {sum}"); // 手动将指针转成ANSI字符串 IntPtr strPtr = SM_Interop_API_getLanguage(); string result = Marshal.PtrToStringAnsi(strPtr); Console.WriteLine($"Sm Language: {result}"); Console.WriteLine("----EOP----"); } }
方案2:缓冲区传递(适合动态生成的字符串,无内存泄漏风险)
该方案由C#侧预先分配字符串缓冲区,C++直接将内容写入缓冲区,内存完全由C#管理,兼容性最好。
修改后C++导出代码
extern "C" { __declspec(dllexport) int SM_Interop_API_Add(int a, int b) { return a + b; } __declspec(dllexport) void SM_Interop_API_getLanguage(char* buffer, int bufferLength) { const char* targetStr = "This is Test String"; // 安全拷贝,避免缓冲区溢出 strncpy_s(buffer, bufferLength, targetStr, _TRUNCATE); } }
修改后C#调用代码
class Program { [DllImport(@"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SM_Interop_API_Add")] public static extern int SM_Interop_API_Add(int a, int b); [DllImport(@"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "SM_Interop_API_getLanguage")] public static extern void SM_Interop_API_getLanguage(StringBuilder buffer, int bufferLength); static void Main(string[] args) { Console.WriteLine("Hello World!"); int sum = SM_Interop_API_Add(10, 10); Console.WriteLine($"Result: {sum}"); // 预先分配足够大的缓冲区 StringBuilder sb = new StringBuilder(256); SM_Interop_API_getLanguage(sb, sb.Capacity); string result = sb.ToString(); Console.WriteLine($"Sm Language: {result}"); Console.WriteLine("----EOP----"); } }
注意事项
- C/C导出函数用于跨语言互操作时,仅可使用C兼容的原生类型,禁止传入或返回
std::string、std::vector等C专属类型。 - 如果需要返回C++侧动态分配的字符串,需配套导出内存释放函数,避免内存泄漏:
__declspec(dllexport) void SM_Interop_FreeString(char* ptr) { free(ptr); }
内容的提问来源于stack exchange,提问作者Shivam
相关产品推荐
相关产品推荐

