【问题标题】:C++ To C# Marshaling of Nested Structs with UnionC++ 到 C# 使用联合对嵌套结构进行编组
【发布时间】:2021-05-08 21:42:36
【问题描述】:

我必须将 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:无法加载类型“XXX.IRF_SAVEDATA_T”,因为它 包含偏移量 0 的对象字段,该对象字段未正确对齐或 被非对象字段重叠

我认为错误的来源是:

[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# struct 中获取 roi 字段的另一种方法。

【问题讨论】:

  • 忽略它并手动将其编组到/从原始字节?
  • 我不知道该怎么做。这是我第一次尝试从 C# 中使用 C++
  • 你知道它是怎么包装的吗?查看您的内部struct,一个以char 开头并具有四个shorts 的结构将根据您的打包和对齐方式具有非常不同的布局。看起来您正在将那些shorts 映射到bytes。短裤是 16 位,字节是 8 位
  • 我已经根据制作的字节更改了 roi 结构 -> ushort。错误仍然存​​在。
  • 我不知道打包,我只有dll和它定义结构的头文件

标签: c# c++ marshalling unmanaged


【解决方案1】:

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

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

【讨论】:

  • 谢谢,但是 roi[2] 呢?我在 c# 中为它创建了单独的结构是正确的吗?
  • 再次感谢,我重写了整个结构层次结构并成功地从c#调用了这个方法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-11
相关资源
最近更新 更多