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
任何地方,您可以像往常一样定义您的字段。
我必须将 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
任何地方,您可以像往常一样定义您的字段。