使用可变长度的结构数组调用/封送
本文关键字:数组 结构 调用 封送 | 更新日期: 2023-09-27 17:50:31
在过去的几天里,我一直在努力用c#编组一个结构。希望有更多经验的人可以帮助(结构定义缩短了一点,所以没有那么多的阅读)。
C HBAAPI定义
HBA_STATUS HBA_GetFcpTargetMapping(
HBA_HANDLE handle,
HBA_FCPTARGETMAPPING *pmapping
);
typedef struct HBA_FCPTargetMapping {
HBA_UINT32 NumberOfEntries;
HBA_FCPSCSIENTRY entry[1]; /* Variable length array
* containing mappings */
} HBA_FCPTARGETMAPPING, *PHBA_FCPTARGETMAPPING;
typedef struct HBA_FcpScsiEntry {
HBA_SCSIID ScsiId;
} HBA_FCPSCSIENTRY, *PHBA_FCPSCSIENTRY;
typedef struct HBA_ScsiId {
char OSDeviceName[256];
HBA_UINT32 ScsiBusNumber;
} HBA_SCSIID, *PHBA_SCSIID;
我在c#中的定义是:
[DllImport("hbaapi.dll")]
static extern Uint32 HBA_GetFcpTargetMapping(
IntPtr handle,
IntPtr fcpmapping
);
[StructLayout(LayoutKind.Sequential)]
public struct HBA_FCPTARGETMAPPING
{
public Uint32_ NumberOfEntries,
public HBA_FCPSCSIENTRY SCSIEntry
}
[StructLayout(LayoutKind.Sequential)]
public struct HBA_FCPSCSIENTRY
{
public HBA_SCSIID ScsiId
}
[StructLayout(LayoutKind.Sequential)]
public struct HBA_SCSIID
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
byte[] OSDeviceName;
Uint32 ScsiBusNumber;
}
我可以得到第一个SCSIEntry,但不能得到后续的。我知道定义是一个可变长度数组,但是我不知道如何正确地声明它,或者将数据封送回Managed Structure。
下面的工作,但显然只有1 SCSIEntry
//Allocate only one, supposed to recall with the appropriate allocated size, using NumberOfEntries
IntPtr buffer = Marshal.AllocHglobal(Marshal.SizeOf(typeof(HBA_FCPTARGETMAPPING)));
Uint32 status = HBA_GetFcpTargetMapping(hbaHandle, buffer);
HBA_FCPTARGETMAPPING fcpTgtMapping = (HBA_FCPTARGETMAPPING)Marshal.PtrtoStructure(buffer, typeof(HBA_FCPTARGETMAPPING));
编辑——这看起来对吗?我如何获得SCSIEntry数组?
[StructLayout(LayoutKind.Sequential)]
public struct HBA_FCPTARGETMAPPING
{
public UInt32 NumberOfEntries;
public IntPtr SCSIEntry; /* Variable length array containing mappings*/
}
//Alloc memory for 1 FCPTargetMapping to get the number of entries
int singleBufferSize = Marshal.SizeOf(typeof(HBA_FCPTARGETMAPPING));
IntPtr singleBuffer = Marshal.AllocHGlobal(singleBufferSize);
uint singleResult = HBA_GetFcpTargetMapping(hbaHandle, singleBuffer);
HBA_FCPTARGETMAPPING singleFCPTargetMapping = (HBA_FCPTARGETMAPPING)Marshal.PtrToStructure(singleBuffer, typeof(HBA_FCPTARGETMAPPING));
int numberOfEntries = int.Parse(singleFCPTargetMapping.NumberOfEntries.ToString());
//more memory required
if (singleResult == 7)
{
//Now get the full FCPMapping
int fullBufferSize = Marshal.SizeOf(typeof(HBA_FCPTARGETMAPPING)) + (Marshal.SizeOf(typeof(HBA_FCPSCSIENTRY)) * numberOfEntries);
IntPtr fullBuffer = Marshal.AllocHGlobal(fullBufferSize);
uint fullResult = HBA_GetFcpTargetMapping(hbaHandle, fullBuffer);
if (fullResult == 0)
{
HBA_FCPTARGETMAPPING fullFCPTargetMapping = (HBA_FCPTARGETMAPPING)Marshal.PtrToStructure(fullBuffer, typeof(HBA_FCPTARGETMAPPING));
//for (uint entryIndex = 0; entryIndex < numberOfEntries; entryIndex++)
//{
//}
}
}
您无法让封送程序封送变长结构。它根本无法做到这一点。这意味着您需要手动编组它。
- 使用
AllocHGlobal
或AllocCoTaskMem
分配结构体 - 在计算结构体的大小时,请确保您考虑了任何填充。
- 使用
PtrToStructure
和StructureToPtr
和指针算法手动封送数组。
有一个带有单个数组元素的结构体是非常值得的。就像问题中的代码一样。您可以使用它来封送结构的主要部分,并让封送程序处理布局和填充。然后使用OffsetOf
查找可变长度数组的偏移量,并逐个元素封送该元素。