C++ 到 C# 使用 Union 编组嵌套结构

C++ To C# Marshaling of Nested Structs with Union

我必须将 C++ .dll 与 C# 代码一起使用。我需要调用一个方法:

THERMALSDK_API short PASCAL GetIRHeaders(HANDLE handle, IRF_IR_FILE_HEADER_T* header, IRF_IR_DATA_HEADER_T* addedInfo, unsigned long *curPos);

为此,我创建了一个简单的包装器:

public class COXAccessor : ICOXAccessor
{
    [DllImport("ThermalCamDll", CallingConvention = CallingConvention.StdCall, ExactSpelling = false,
        EntryPoint = "GetIRHeaders")]
    private static extern ReturnCode GetIRHeaders(out IntPtr handle, out IRF_IR_FILE_HEADER_T header, out IRF_IR_DATA_HEADER_T addedInfo, out uint curPos);

    public ReturnCode GetIRHeadersInternal(out IntPtr handle, out IRF_IR_FILE_HEADER_T header,
        out IRF_IR_DATA_HEADER_T addedInfo, out uint curPos)
    {
        return GetIRHeaders(out handle, out header, out addedInfo, out curPos);
    }
}

如您所见,此方法需要传入的对象很少。其中最复杂的是 IRF_IR_DATA_HEADER_T,它嵌套结构 IRF_SAVEDATA_T:

/* Structure of IR data header */
typedef struct  
{
    BYTE                    dynamic_range;          // IRF_DYNAMIC_RANGE_T 
  
    IRF_SAVEDATA_T                  save_data;      // Cam data in CAM_DATA

    BYTE                    reserved[460];
}   IRF_IR_DATA_HEADER_T;

IRF_SAVEDATA_T(部分):

typedef struct strSAVEDATA
{
    union {
        struct
        {
            unsigned int        crc;                //  CRC Data
            unsigned char       ver;                //  Setup Data Version  ( CG Model : 0x20 )
            unsigned char       sensor;             //  Sensor Type ( 0x00 : CX320, 0x01 : CX640, 0x20 : CG QVGA, 0x21 : CG VGA )
            
            unsigned char       show_isotherm;      //  CX Model Only
            unsigned char       alarm1_duration;    //  CX Model Only
            unsigned char       alarm2_duration;    //  CX Model Only

            struct {
                unsigned char flag;                 //  ROI Function Mask ( 0x01 : Enable, 0x02 : Exclude )
                unsigned short x1;                  //  Position (x2)
                unsigned short y1;                  //  Position (x2)
                unsigned short x2;                  //  Position (x2)
                unsigned short y2;                  //  Position (x2)
            }   roi[2];                             //  CX Model Only
        };

           unsigned char        reserved1[128];
    };
}   IRF_SAVEDATA_T;

我知道 C# 不支持联合,替代它们的方法是使用 FieldOffset 属性。所以我像这样重新创建了这个结构:

[StructLayout(LayoutKind.Explicit)]
public struct IRF_SAVEDATA_T
{

[FieldOffset(0)] public uint crc;

[FieldOffset(0)] public byte ver;

[FieldOffset(0)] public byte sensor;

[FieldOffset(0)] public byte show_isotherm;

[FieldOffset(0)] public byte alarm1_duration;

[FieldOffset(0)] public byte alarm2_duration;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] [FieldOffset(0)] public roi[] roi;

但是当我尝试启动程序时弹出错误:

TypeLoadException: Could not load type 'XXX.IRF_SAVEDATA_T' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field

我认为错误的来源是:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] [FieldOffset(0)] public roi[] roi;

为了在 C# 中重新创建这个初始化数组 roi 的匿名结构,我创建了新结构:

[StructLayout(LayoutKind.Sequential)]
public struct roi
{
    /// <summary>
    /// ROI Function Mask ( 0x01 : Enable, 0x02 : Exclude )
    /// </summary>
    public byte flag;

    /// <summary>
    /// Position (x2)
    /// </summary>
    public ushort x1;

    /// <summary>
    /// Position (x2)
    /// </summary>
    public ushort y1;

    /// <summary>
    /// Position (x2)
    /// </summary>
    public ushort x2;

    /// <summary>
    /// Position (x2)
    /// </summary>
    public ushort y2;
}

我认为这是错误的,但我不知道在 C# 结构中获取 roi 字段的另一种方法。

忘记联合,在这种情况下它什么都不做,只是在末尾添加填充。填充显然很重要,但您可以通过计算字节数并确保您的结构至少为 128 字节(这是第二个联合部分将其设置为)来自己填充它。

这将大大简化您的定义,不再需要 FieldOffset 任何地方,您可以像往常一样定义您的字段。