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

如何通过PInvoke从C#向C库传递数组指针并正确获取数据

解决C#调用C DLL中getData函数的内存写入问题

首先注意到你提供的C++代码中调用的是getFrame,但C库函数声明是getData,这大概率是笔误,请先确认C#代码里的函数名与DLL导出的函数名完全一致(可通过dumpbin /exports工具查看DLL的导出表)。

下面是几种正确的调用方式,以及内存验证方法:

方法一:修正数组封送特性与函数签名

默认情况下,C#将值类型数组作为[In]参数封送,仅传递数据到非托管代码,不会回传修改后的数据。即使添加[In, Out],若函数签名或调用约定不匹配也会失效,请重点检查以下几点:

  1. 确保DLL的调用约定为cdecl(与你指定的一致)
  2. 函数返回值类型匹配:C++的uint对应C#的uint而非int,错误的返回值类型会导致调用栈混乱,进而引发内存异常
  3. 显式指定数组的封送特性与固定长度

修正后的代码:

[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl, EntryPoint = "getData")]
static private extern uint getData([In, Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 4096)] ushort[] buffer, ushort dataIndex);

ushort[] buffer = new ushort[4096];
// 调用前填充固定值,方便验证是否被修改
Array.Fill(buffer, (ushort)0xFFFF);
uint response = getData(buffer, 1);
// 检查前10个元素的变化
for(int i = 0; i < 10; i++)
{
    Console.WriteLine(buffer[i]);
}

方法二:使用IntPtr手动管理非托管内存

这种方式可完全控制内存分配,规避自动封送的潜在问题:

[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
static private extern uint getData(IntPtr buffer, ushort dataIndex);

// 分配非托管内存:4096个ushort,每个占2字节
IntPtr unmanagedBuffer = Marshal.AllocHGlobal(4096 * sizeof(ushort));
try
{
    // 可选:初始化非托管内存为固定标记值
    ushort[] initBuffer = new ushort[4096];
    Array.Fill(initBuffer, (ushort)0xAAAA);
    Marshal.Copy(initBuffer, 0, unmanagedBuffer, 4096);

    uint response = getData(unmanagedBuffer, 1);

    // 将非托管内存数据复制回托管数组
    ushort[] resultBuffer = new ushort[4096];
    Marshal.Copy(unmanagedBuffer, resultBuffer, 0, 4096);

    // 验证数据
    Console.WriteLine("First 5 elements:");
    for(int i = 0; i < 5; i++)
    {
        Console.WriteLine(resultBuffer[i]);
    }
}
finally
{
    // 必须释放非托管内存,避免泄漏
    Marshal.FreeHGlobal(unmanagedBuffer);
}

方法三:使用unsafe代码直接传递指针

若项目允许启用unsafe代码,可直接获取托管数组的指针:

[DllImport(DllPath, CallingConvention = CallingConvention.Cdecl)]
static private extern unsafe uint getData(ushort* buffer, ushort dataIndex);

unsafe
{
    ushort[] buffer = new ushort[4096];
    Array.Fill(buffer, (ushort)0xBBBB);
    fixed(ushort* ptr = buffer)
    {
        uint response = getData(ptr, 1);
    }
    // 直接检查buffer数组即可
    foreach(var val in buffer.Take(5))
    {
        Console.WriteLine(val);
    }
}

验证内存是否被正确写入的方法

  • 初始化标记法:调用前将数组填充固定非随机值(如0xFFFF、0xAAAA),调用后检查是否有值被修改,若仍为初始值说明内存未被写入;若变为其他值,再对比是否符合预期数据
  • 调试器内存查看:C#调试时,在调用后设置断点,通过Memory窗口查看IntPtr或unsafe指针指向的内存内容,与C++调试时的内存数据对比
  • 文件对比法:写简单的C++测试程序,调用getData后将buffer数据写入文件;C#调用后将数据写入同格式文件,对比两个文件是否一致

内容的提问来源于stack exchange,提问作者Iridium

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:23:13