You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PInvoke受保护内存读写错误求助:C++ DLL返回时触发异常

解决PInvoke保护内存错误的问题

哥们,我帮你揪出这个错误的核心根源了,咱们一步步来搞定它:

问题本质

当C#的P/Invoke接收返回值为string时,默认会认为这块内存是通过CoTaskMemAlloc分配的——处理完字符串后,它会自动调用CoTaskMemFree去释放内存。但你的C代码用的是new char[]分配内存,这是C CRT的分配器,和C#默认期望的分配器完全不兼容。所以C#尝试释放不属于它管理的内存,直接触发了“保护内存”的错误。

你切换调用约定没用,因为内存管理不匹配才是真凶。

三种解决方案

方案一:让C++用C#兼容的内存分配器

修改你的C++代码,用CoTaskMemAlloc替代new char[],这样C#就能正确识别并释放内存:

#include <objbase.h> // 必须包含这个头文件才能使用CoTaskMemAlloc

// 你的原有逻辑...
ans += ";"; ans += rans; 
cout << "returning value 2" << endl; 
size_t strLen = ans.size() + 1;
char* retval = (char*)CoTaskMemAlloc(strLen);
strcpy_s(retval, strLen, ans.c_str()); 
return retval;

改完后,你的C#调用代码不用动就能正常工作。

方案二:让C#负责分配内存(更安全推荐)

这种方式完全避免跨语言内存管理冲突,让C#提前分配好缓冲区,传递给C++函数填充:

C++端修改函数:

// 加extern "C"避免C++名字改编
extern "C" __declspec(dllexport) void Stop_Record(char* buffer, int bufferSize) {
    ans += ";"; ans += rans; 
    cout << "returning value 2" << endl;
    // 用strcpy_s确保不会缓冲区溢出
    strcpy_s(buffer, bufferSize, ans.c_str());
}

C#端修改PInvoke声明和调用:

[DllImport("C:\\Users\\Boaz\\source\\repos\\Macro_Setter_DLL\\Debug\\Macro_Setter_DLL.dll", CallingConvention = CallingConvention.StdCall)]
static extern void Stop_Record(StringBuilder buffer, int bufferSize);

// 调用示例
StringBuilder resultBuffer = new StringBuilder(1024); // 按需调整缓冲区大小
Stop_Record(resultBuffer, resultBuffer.Capacity);
string tmp = resultBuffer.ToString();

这个方案最安全,内存完全由调用方(C#)掌控,不会出现泄漏或释放错误。

方案三:手动管理内存(适合特殊场景)

如果不想改C的分配方式,可以返回IntPtr,在C#里手动处理字符串,最后调用C提供的释放函数清理内存:

C++端新增释放函数:

extern "C" __declspec(dllexport) void FreeRecordString(char* str) {
    delete[] str; // 和new char[]对应释放
}

C#端修改声明和调用:

[DllImport("你的DLL路径", CallingConvention = CallingConvention.StdCall)]
static extern IntPtr Stop_Record();

[DllImport("你的DLL路径", CallingConvention = CallingConvention.StdCall)]
static extern void FreeRecordString(IntPtr ptr);

// 调用
IntPtr strPtr = Stop_Record();
string tmp = Marshal.PtrToStringAnsi(strPtr);
FreeRecordString(strPtr); // 必须手动释放,否则会内存泄漏

总结

优先选方案二,其次是方案一,方案三尽量少用(容易忘释放导致泄漏)。跨语言调用最容易栽在内存管理上,核心就是要确保分配内存的方式和释放内存的方式完全匹配~

内容的提问来源于stack exchange,提问作者Monty6400

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:52:35