将复杂结构从 C# 编组到 C

Marshalling a complex structure from C# to C

我正在阅读有关 64 位环境中 C 和 C# 之间的结构编组的所有内容,但没有成功。

我需要做的是传递结构

typedef struct DescStructTa
{
    char*           pszNa;
    unsigned long   ulTId;      
    char*           pszT;
    unsigned short  usRId;
    unsigned long   ulOff;
    unsigned long   ulSi;
    char            szAcc[2];
    unsigned char   bySSize;
} DescStruct;

从 C# 应用程序到不是我们调用方法制作的 C DLL

MT_DLL_DECL long GetAll(UINTPTR ulHandler, DescStruct **ppList, unsigned long *pulNumS);

经过长时间的搜索,我编写了以下代码:

[StructLayout(LayoutKind.Sequential, Pack = 8)]
    public struct PlcSymbolDescStruct
    {
        [MarshalAs(UnmanagedType.LPStr)]
        string pszNa;
        UInt32 ulTId;
        [MarshalAs(UnmanagedType.LPStr)]
        string pszT;
        [MarshalAs(UnmanagedType.U2)]
        ushort usRId;
        UInt32 ulOff;
        UInt32 ulSi;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
        string szAcc;
        [MarshalAs(UnmanagedType.U1)]
        byte bySSize;
    }

    [DllImport("DataSource.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.U8)]public static extern long GetAll(ulong ulHandler, out DescStruct [] ppList,
         out uint pulNumS);

不幸的是,这仍然给我造成堆损坏。 如果我尝试打包少于 8 个,它会(如预期的那样)出现访问冲突,但我预计这会起作用,但我无法弄清楚可能出了什么问题。

有什么建议吗?

提前致谢!

我不确定你是否需要 Pack = 8。默认打包似乎或多或少在 C# 和 C++ 之间兼容。

szAcc 字段可能使用 Unicode。要切换,请在 [StructLayout]

中指定 CharSet=CharSet.Ansi

最后,如果您的 C API 具有指向它们数组的双指针,则您必须以特殊方式分配该内存,CoTaskMemAlloc 或 LocalAlloc,忘记是哪个了。 相反,实现 2 个函数更容易,也更有效。一是获得所需大小的数组。另一个将它们写入调用者分配的缓冲区,C++ 中的单个指针而不是双指针,以及 C# 中的 UnmanagedType.LPArray

更新

allocation should not be necessary since the documentation states that the call returns a pointer to it's internal data structure

运行时不知道该文档中写了什么,无论如何尝试释放,然后崩溃。您可以在 C# API 中使用 out IntPtr,并手动编组。

如果您有现代 .NET,请使用不安全,将 IntPtr 转换为 PlcSymbolDescStruct* 原始指针,并从该原始指针构造 ReadOnlySpan<PlcSymbolDescStruct>

如果您坚持使用旧的桌面 .NET,请在循环中调用 Marshal.PtrToStructure<PlcSymbolDescStruct>,在循环迭代之间将指针递增 Marshal.SizeOf<PlcSymbolDescStruct>() 字节。在这种情况下无需使用不安全代码。