从C++ DLL向C#客户端传递字节数组时返回随机值的问题排查
问题根源
嘿,我一眼就看出问题出在哪了——你在C++函数里返回了局部数组的指针!
当passBytes()函数执行完毕后,栈上的局部变量bytes会被销毁,对应的内存空间会被操作系统回收或覆盖。这时候你返回的指针bptr就变成了“野指针”,指向的是已经无效的内存区域,所以C#这边用Marshal.Copy读取时,拿到的自然是随机垃圾值。
解决方案
下面给你三种可行的修复方案,按推荐程度排序:
方案3:由C#提供缓冲区(最安全推荐)
这种方式让C#预先分配好字节数组,把数组指针传递给C函数,C直接写入缓冲区,完全避免内存管理问题,是业界更推荐的做法:
修改后的C++ DLL代码
extern "C" __declspec(dllexport) void __stdcall passBytes(char* buffer, int bufferSize) { cout << "\n passBytes DLL code START \n"; // 先检查缓冲区大小是否足够 if (bufferSize >= 2) { buffer[0] = 0x07; buffer[1] = 0x08; } cout << "bytes to write: " << endl; cout << "byte 0 = 7" << endl; cout << "byte 1 = 8" << endl; cout << "\n passBytes DLL code END \n"; }
修改后的C#客户端代码
namespace sc_client { class Program { const string libname = "C:/work/x64/Release/libMxN.dll"; // 用MarshalAs让CLR自动处理数组与指针的转换 [DllImport(libname)] static extern void passBytes([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] buffer, int bufferSize); static void Main(string[] args) { byte[] cbytes = new byte[2]; // 直接传递数组即可,无需手动处理指针 passBytes(cbytes, cbytes.Length); Console.WriteLine("C# client output: "); for ( int i = 0; i < cbytes.Length; i++) { Console.WriteLine("client byte " + i + " = " + (int)cbytes[i]); } } } }
方案2:动态分配内存(需手动释放)
在C++里用malloc动态分配内存,C#使用完后调用DLL的释放函数回收内存,避免泄漏:
C++ DLL代码
extern "C" __declspec(dllexport) char* __stdcall passBytes() { cout << "\n passBytes DLL code START \n"; char* bytes = (char*)malloc(2); // 动态分配2字节内存 bytes[0] = 0x07; bytes[1] = 0x08; cout << "bytes array size: 2" << endl; for (int i = 0; i < 2; i++) { cout << "byte " << i << " = " << (int)bytes[i] << endl; } cout << "\n passBytes DLL code END \n"; return bytes; } // 新增释放内存的函数 extern "C" __declspec(dllexport) void __stdcall freeBytes(char* ptr) { free(ptr); }
C#客户端代码
namespace sc_client { class Program { const string libname = "C:/work/x64/Release/libMxN.dll"; [DllImport(libname)] static extern IntPtr passBytes(); [DllImport(libname)] static extern void freeBytes(IntPtr ptr); static void Main(string[] args) { IntPtr ptr = passBytes(); byte[] cbytes = new byte[2]; Marshal.Copy(ptr, cbytes, 0, 2); Console.WriteLine("C# client output: "); for ( int i = 0; i < cbytes.Length; i++) { Console.WriteLine("client byte " + i + " = " + (int)cbytes[i]); } freeBytes(ptr); // 必须调用释放,否则会内存泄漏 } } }
方案1:使用静态数组(简单但有线程风险)
把C++的局部数组改成static,数组会被分配在静态存储区,函数返回后内存不会释放,但静态数组是全局共享的,多线程调用会有数据覆盖风险,仅适合单线程场景:
C++ DLL代码
extern "C" __declspec(dllexport) char* __stdcall passBytes() { cout << "\n passBytes DLL code START \n"; static char bytes[] = { 0x07, 0x08 }; // 添加static关键字 cout << "bytes array size: " << sizeof(bytes) << endl; for (int i = 0; i < sizeof(bytes); i++) { cout << "byte " << i << " = " << (int)bytes[i] << endl; } char* bptr = bytes; cout << "\n passBytes DLL code END \n"; return bptr; }
内容的提问来源于stack exchange,提问作者34n
相关产品推荐
相关产品推荐

