具有可变长度结构数组的PInvoke /编组

时间:2015-05-23 01:57:40

标签: c# pinvoke marshalling

在过去的几天里,我一直在努力用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,但不能获得后续的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 个答案:

答案 0 :(得分:0)

你无法让marshaller编组一个可变长度的结构。它根本无法做到这一点。这意味着您需要手动编组。

  1. 使用AllocHGlobalAllocCoTaskMem
  2. 分配结构
  3. 计算结构的大小时,请确保考虑任何填充。
  4. 使用PtrToStructureStructureToPtr以及指针算法手动编组数组。
  5. 使用数组中单个元素的结构非常值得。就像你在问题中的代码中一样。您可以使用它来编组结构的主要部分,并让编组器处理布局和填充。然后使用OffsetOf查找可变长度数组的偏移量,并按元素封送该元素。