I've been struggling with marshalling a structure in C# for the last couple of days. Hoping someone with a bit more experience can assist (structure definitions were shortened a bit so it's not as much reading).
C HBAAPI definition
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;
My definitions in C# are:
[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;
}
I can get the first SCSIEntry, but not subsequent ones. I understand that the definition is a variable length array, but I can't figure out how to declare it properly, or Marshal the data back into the Managed Structure.
The following works, but obviously only gets 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));
Edit -- does this look right? How do I get the array of 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++)
//{
//}
}
}