C#调用C++硬件DLL时Marshal.StructureToPtr内存保护错误疑问
我在C#中通过C++ DLL与硬件通信时,需要借助Marshal.StructureToPtr将结构体转换为指针,但执行以下代码时触发错误:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
错误代码示例:
//Marshal.Copy(bytes, 0, P2request, Marshal.SizeOf(request)); Marshal.StructureToPtr(request, P2request, true);
我已找到两种可行的解决方法,但不清楚背后的技术原因,希望得到解释:
- 添加
Marshal.Copy清理缓冲区后代码正常运行:// it works Marshal.Copy(bytes, 0, P2request, Marshal.SizeOf(request)); Marshal.StructureToPtr(request, P2request, true); - 将
Marshal.StructureToPtr的第三个参数设为false后代码正常运行://it works //Marshal.Copy(bytes, 0, P2request, Marshal.SizeOf(request)); Marshal.StructureToPtr(request, P2request, false);
原因解释
1. fDeleteOld=true引发错误的核心原因
Marshal.StructureToPtr的第三个参数fDeleteOld为true时,CLR会尝试释放目标指针P2request指向内存中已存在的托管对象资源(比如之前通过StructureToPtr写入的结构体包含的字符串、数组等引用类型)。但如果P2request指向的是非CLR管理的原生内存(比如C++ DLL分配的硬件通信缓冲区),这块内存没有CLR托管对象的元数据,CLR尝试释放时就会访问受保护的内存区域,触发内存访问违规错误。
2. Marshal.Copy的作用原理
Marshal.Copy会用bytes数组的内容直接覆盖P2request指向的内存,把之前内存中可能残留的、让CLR误判为托管对象的数据彻底覆盖。当后续StructureToPtr以fDeleteOld=true执行时,CLR无法识别出可释放的托管资源,就不会执行释放操作,因此避免了错误。但这只是临时规避问题的手段,并非正确的处理方式。
3. fDeleteOld=false的合理性
既然P2request指向的是原生非托管内存,CLR没有权限也不需要管理这块内存的释放。将fDeleteOld设为false,就是明确告诉CLR:直接覆盖写入结构体数据即可,无需清理目标内存。这完全匹配硬件通信缓冲区的使用场景,是最合理的解决方案。
内容的提问来源于stack exchange,提问作者stone liew

