在 C# 中正确封送 C++ 结构
本文关键字:C++ 结构 | 更新日期: 2023-09-27 18:35:53
我C++有这个结构,我需要转换为 C#,所以我可以从 byte[] 创建这个结构。
struct TRANS_RECORD_DATA {
int size, code;
int ThrowItemCount;
int ItemCount;
int ItemSubStart;
int DataSize;
BYTE Data[sizeof(sRECORD_ITEM) * 200]; // sizeof(sRECORD_ITEM) = 548
};
C# 版本:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TRANS_RECORD_DATA {
public int size, code;
public int ThrowItemCount;
public int ItemCount;
public int ItemSubStart;
public int DataSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 548*200)]
public byte[] Data;
};
我正在使用这个通用函数来为我提供字节数组中的结构:
T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T stuff = (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (T));
handle.Free();
return stuff;
}
但它给了我:
访问冲突异常。
我相信我知道为什么,但我不知道如何解决它。我需要映射到结构中的byte[]
并不总是具有大小为 548*200 的 Data
成员。此数字是最大值。但似乎我使用的通用方法总是尝试使用该数据始终为 548*200 创建结构,然后它显然会抛出 AccessViolation ,因为要映射的数据已经结束。
例如,此代码:
var bytes = new byte[26];
var structure = ByteArrayToStructure<TRANS_RECORD_DATA>(bytes);
应该返回一个TRANS_RECORD_DATA
,其中包含所有值为 0 的 int 成员,最后该byte[] Data
将只有剩余的两个字节。(26 - 24 = 2)。但它似乎一直试图创建完整的 548*200 字节[],然后导致访问冲突。
有没有办法解决这个问题?
因此,当您解释它时,无论数据数组的 C 定义长度为 200*548 字节,它实际上并没有完全由您调用的外部非托管代码分配或填充。
这样,唯一的解决方法是不在 C# 结构定义中定义 Data:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TRANS_RECORD_DATA {
public int size, code;
public int ThrowItemCount;
public int ItemCount;
public int ItemSubStart;
public int DataSize;
};
并在读取 DataSize 后将字节数组的其余部分重新解释为该数据结构。
您仍然可以使用对 Marshal.PtrToStructure 的一系列调用:
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
TRANS_RECORD_DATA stuff = (TRANS_RECORD_DATA) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (TRANS_RECORD_DATA));
var items = new List<sRECORD_ITEM>(stuff.ItemCount);
for (int i = 0; i < stuff.ItemCount; ++i)
{
var ptr = handle.AddrOfPinnedObject().Add(i*548)
sRECORD_ITEM item = (sRECORD_ITEM)Marshal.PtrToStructure(ptr,typeof(sRECORD_ITEM));
}
我很确定这应该可以解决问题。