C#调用C++ DLL中I15693_Inventorys方法时,OUT参数pData无法获取返回值的问题求助
我之前也踩过类似的P/Invoke参数匹配的坑,咱们一步步拆解解决:
问题根源分析
先看C++的函数声明:
DECLSPEC int WINAPI I15693_Inventorys(HID_DEVICE device, BYTE *pData, BYTE *pLen);
这里的BYTE* pData是输出参数,C++函数会往这块内存地址写入标签数据;BYTE* pLen是输入输出参数——你需要先传入pData缓冲区的大小,函数执行后会返回实际写入的数据长度。
厂商给的C#声明里,pData的问题出在传递方向上:C#中byte[]作为P/Invoke参数时,默认是[In]方向——只会把C#数组的数据传给C++,但C写入的数据不会回传到C#数组里。而你直接给pData加ref修饰符的话,相当于把byte[]对象的指针(也就是byte**)传给了期望byte*的C函数,类型不匹配直接导致内存访问崩溃。
解决方案
1. 修正P/Invoke声明
给pData参数加上[Out]属性(如果函数需要先读取缓冲区大小,就用[In, Out]),明确告诉CLR这个参数是输出方向:
[DllImport("你的DLL文件名.dll", CallingConvention = CallingConvention.StdCall)] public static extern int I15693_Inventorys(IntPtr device, [Out] byte[] pData, ref byte pLen);
注意:要确保
CallingConvention和C++的WINAPI(即stdcall)匹配,虽然默认通常是这个,但显式指定能避免很多隐性问题。
2. 预先分配足够的缓冲区
你当前代码里chip是空数组,C++函数根本没有内存空间写入数据!根据ISO 15693协议的Inventory命令,返回的标签数据长度不会太长,先分配一个足够大的缓冲区:
// 先分配256字节的缓冲区,足够容纳多数15693标签的Inventory数据 byte[] chip = new byte[256]; // 初始化pLen为缓冲区的长度,告诉C++函数我们提供的内存大小 byte longitud = (byte)chip.Length;
3. 调用函数并处理返回数据
调用后,longitud会被更新为实际写入的数据长度,你可以截取数组的有效部分:
_ = RFIDReader.Sys_SetAntenna(g_hDevice, 0); _ = RFIDReader.Sys_InitType(g_hDevice, 1); _ = RFIDReader.Sys_SetAntenna(g_hDevice, 1); int resultado = RFIDReader.I15693_Inventorys(g_hDevice, chip, ref longitud); if (resultado == 0) { // 截取实际有效数据 byte[] validChipData = new byte[longitud]; Array.Copy(chip, validChipData, longitud); // 现在validChipData就是你要的返回值了 }
补充:为什么加ref会崩溃?
C的BYTE*是一级指针(指向字节数组的首地址),而C#中ref byte[]传递的是二级指针(指向byte[]对象的指针)。当C函数尝试用一级指针的方式去操作这个二级指针时,必然会访问错误的内存地址,直接触发崩溃。
内容的提问来源于stack exchange,提问作者user17779610




