问题描述
我必须将 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 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# 结构中获取 roi 字段的方法。
解决方法
忘记联合,在这种情况下它什么都不做,只是在最后添加填充。填充显然很重要,但您可以通过计算字节数并确保您的结构至少为 128 字节(这是第二个联合部分将其设置为)来自己填充它。
这将大大简化您的定义,任何地方都不再需要 FieldOffset
,您只需像平常一样定义您的字段即可。