使用可变长度的结构数组调用/封送

本文关键字:数组 结构 调用 封送 | 更新日期: 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++)
        //{
         //}
    }
}

使用可变长度的结构数组调用/封送

您无法让封送程序封送变长结构。它根本无法做到这一点。这意味着您需要手动编组它。

  1. 使用AllocHGlobalAllocCoTaskMem分配结构体
  2. 在计算结构体的大小时,请确保您考虑了任何填充。
  3. 使用PtrToStructureStructureToPtr和指针算法手动封送数组。

有一个带有单个数组元素的结构体是非常值得的。就像问题中的代码一样。您可以使用它来封送结构的主要部分,并让封送程序处理布局和填充。然后使用OffsetOf查找可变长度数组的偏移量,并逐个元素封送该元素。