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

从C#(.NET Core)向非托管C++传递结构体数组失败求助

你的核心问题在于C和C#之间的参数类型不匹配——C函数期望的是指向结构体指针的数组(LOG_SAVE_DETAILS**),但你在C#里直接传递了SaveDetails[](对应C++的连续结构体数组LOG_SAVE_DETAILS[]),这两种类型在内存布局上完全不同,自然会导致访问违例。

下面是一步步的解决方案:

1. 修正结构体定义(优先用struct而非class)

结构体是值类型,封送时更贴合C++结构体的语义,同时确保字段和布局完全匹配:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SaveDetails
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string Log;
    public FILETIME FileTime;
    [MarshalAs(UnmanagedType.Bool)]
    public bool Saved;
}

// 确保FILETIME和C++的定义完全一致
[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
    public uint dwLowDateTime;
    public uint dwHighDateTime;
}
2. 调整委托和调用逻辑,手动构建指针数组

因为C++需要的是LOG_SAVE_DETAILS**,我们需要手动为每个结构体分配非托管内存、拷贝数据,再把这些指针组成数组传递给函数,调用完成后务必释放内存避免泄漏。

首先修改委托的参数类型:

[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles(IntPtr logsToSavePtrArray, string destinationPath);

然后重写SaveFiles方法:

public Status SaveFiles(SaveDetails[] logsToSave, string destinationPath)
{
    Status result = Status.FunctionNotAvailable;
    if (!LogSaveFilesAvailable)
        return result;

    _dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
    
    // 为每个结构体分配非托管内存并拷贝数据
    IntPtr[] structPointers = new IntPtr[logsToSave.Length];
    for (int i = 0; i < logsToSave.Length; i++)
    {
        structPointers[i] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SaveDetails)));
        Marshal.StructureToPtr(logsToSave[i], structPointers[i], false);
    }

    try
    {
        // 分配内存存储指针数组
        IntPtr ptrArray = Marshal.AllocHGlobal(IntPtr.Size * structPointers.Length);
        try
        {
            // 把指针数组拷贝到非托管内存
            Marshal.Copy(structPointers, 0, ptrArray, structPointers.Length);
            // 调用C++函数
            result = _dLogSaveFiles(ptrArray, destinationPath);
        }
        finally
        {
            // 释放指针数组的内存
            Marshal.FreeHGlobal(ptrArray);
        }
    }
    finally
    {
        // 释放每个结构体的非托管内存
        foreach (IntPtr ptr in structPointers)
        {
            if (ptr != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(ptr);
            }
        }
    }

    return result;
}
3. 验证调用约定和字符集一致性
  • 确保C++的_API宏对应的是__stdcall调用约定(和C#的CallingConvention.Winapi匹配,Windows平台下Winapi等价于stdcall)。
  • 字符集要统一:如果C++项目编译时用的是Unicode字符集(LPTSTR等价于wchar_t*),那么C#的CharSet.Unicode是正确的;如果是多字节字符集,需要改成CharSet.Ansi。
额外提示

如果你的C++函数会修改结构体中的字段(比如Saved标记),调用Marshal.StructureToPtr时第三个参数要设为true,并且在函数调用完成后,用Marshal.PtrToStructure把修改后的非托管数据拷贝回托管结构体。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:31:28