如何在C# P/Invoke中正确封送LPDWORD类型参数?
正确封送LPDWORD参数的解决方案
首先明确C函数中第4个参数LPDWORD lpdwBuffer的含义:它是指向DWORD类型缓冲区的指针,函数会向该缓冲区写入wNumChans个DWORD数据(对应C#的int或uint类型)。结合你的C调用示例,传入的是数组指定位置的地址,本质是让函数从该地址开始连续写入数据。
方案一:直接使用托管数组(推荐)
这是最简洁的方式,通过MarshalAs属性告诉CLR如何正确处理数组封送:
修正后的C# DllImport声明
[System.Runtime.InteropServices.DllImport("MCBCIO32.dll", EntryPoint = "MIOGetData", CharSet = System.Runtime.InteropServices.CharSet.Ansi)] public static extern IntPtr MIOGetData( int hDet, ushort wStartChan, ushort wNumChans, [System.Runtime.InteropServices.Out, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPArray, SizeParamIndex = 2)] int[] lpdwBuffer, ref ushort lpwRetChans, ref int lpdwDataMask, ref int lpdwROIMask, string lpszAuth);
关键说明:
[Out]:标记该参数为输出参数,CLR会将非托管内存中的数据回写到托管数组。SizeParamIndex = 2:指定第3个参数(索引从0开始)是数组的元素数量,CLR会据此确定要封送的元素个数。CharSet.Ansi:匹配C++的LPCSTR(ANSI字符串),避免字符编码错误。- 类型匹配:C++的
WORD对应C#的ushort,替换之前的short更准确。
调用示例
// 定义参数 ushort startChan = 0; ushort numChans = 1; int[] buffer = new int[numChans]; // 数组大小至少等于要获取的通道数 ushort retChans = 0; int dataMask = 0; int roiMask = 0; string auth = pUConn.GetAuthorization(); // 调用函数 MCBWrapper.MIOGetData(hDet, startChan, numChans, buffer, ref retChans, ref dataMask, ref roiMask, auth); // 输出结果 foreach (int val in buffer) { Console.Write($"{val} "); }
方案二:手动分配非托管内存(适合复杂场景)
如果需要更精细的内存控制,可以使用IntPtr手动分配内存:
修正后的C# DllImport声明
[System.Runtime.InteropServices.DllImport("MCBCIO32.dll", EntryPoint = "MIOGetData", CharSet = System.Runtime.InteropServices.CharSet.Ansi)] public static extern IntPtr MIOGetData( int hDet, ushort wStartChan, ushort wNumChans, IntPtr lpdwBuffer, ref ushort lpwRetChans, ref int lpdwDataMask, ref int lpdwROIMask, string lpszAuth);
调用示例
ushort startChan = 0; ushort numChans = 1; int arrSize = numChans; // 必须与要获取的通道数一致,避免读取未初始化内存 int[] buffer = new int[arrSize]; ushort retChans = 0; int dataMask = 0; int roiMask = 0; // 分配足够的非托管内存 IntPtr pBuffer = System.Runtime.InteropServices.Marshal.AllocHGlobal(arrSize * sizeof(int)); // 调用函数 MCBWrapper.MIOGetData(hDet, startChan, numChans, pBuffer, ref retChans, ref dataMask, ref roiMask, ""); // 将非托管内存的数据复制到托管数组 System.Runtime.InteropServices.Marshal.Copy(pBuffer, buffer, 0, arrSize); // 释放非托管内存 System.Runtime.InteropServices.Marshal.FreeHGlobal(pBuffer); // 输出结果 foreach (int val in buffer) { Console.Write($"{val} "); }
为什么之前的方法失败?
ref int[]:会被封送为int**(指向数组指针的指针),与函数需要的int*类型不匹配,导致数据无法正确写入。- 直接使用
int[]但未加[Out]和SizeParamIndex:CLR无法识别这是输出参数,也不知道要处理多少元素,导致返回全0。 - 固定大小的
IntPtr分配:你之前用了固定的100个元素,但函数仅写入wNumChans个元素,剩余内存是随机值,所以输出错误数据。
内容的提问来源于stack exchange,提问作者Isaac
相关产品推荐
相关产品推荐

