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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 00:37:51