Search code examples
c#pinvokemarshallingunmanagedmanaged

How to get unmanaged variable-length C-array within a struct from C to C#?


How do I return the MIB_IPFORWARDROW array?

struct MIB_IPFORWARDTABLE
{
    public uint Size;

    [MarshalAs(/* what goes here? */)]
    public IPFORWARDROW[] Table;
};

[DllImport("iphlpapi", CharSet = CharSet.Auto)]
private static extern int GetIpForwardTable(
    IntPtr /* MIB_IPFORWARDTABLE* */ pIpForwardTable,
    ref uint /* ULONG* */ pdwSize,
    bool bOrder);

public static MIB_IPFORWARDROW[] Temp()
{
    var fwdTable = IntPtr.Zero;
    uint size = 0;
    var result = GetIpForwardTable(fwdTable, ref size, true);
    fwdTable = Marshal.AllocHGlobal((int) size);
    result = GetIpForwardTable(fwdTable, ref size, true);

    /*
    what now ?
    I tried:

    var table = (MIB_IPFORWARDTABLE) Marshal.PtrToStructure(fwdTable, typeof(MIB_IPFORWARDTABLE));

    but table.Table is always null
    */
}

(See GetIpForwardTable)

I've seen this: How do I marshal a struct that contains a variable-sized array to C#?

But whatever I do, fwdTable.Table is always null, IntPtr.Zero or 0. That is ofcourse, if no Exception is thrown..


Solution

  • This thread seems to be exactly what you are looking for: http://social.msdn.microsoft.com/Forums/vstudio/en-US/19a3ce21-e395-4151-86f6-723a64272f0d/calling-getipforwardtable-from-c?forum=csharpgeneral

    Their solution employs a brute force loop to copy the raw array into a System.Array of IPFORWARDROW.

    The final code would look something like this:

        [ComVisible(false), StructLayout(LayoutKind.Sequential)]
        internal struct IPForwardTable
        {
            public uint Size;
    
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
            public IPFORWARDROW[] Table;
        };
    
        [ComVisible(false), StructLayout(LayoutKind.Sequential)]
        internal struct IPFORWARDROW
        {
            internal int /*DWORD*/ dwForwardDest;
            internal int /*DWORD*/ dwForwardMask;
            internal int /*DWORD*/ dwForwardPolicy;
            internal int /*DWORD*/ dwForwardNextHop;
            internal int /*DWORD*/ dwForwardIfIndex;
            internal int /*DWORD*/ dwForwardType;
            internal int /*DWORD*/ dwForwardProto;
            internal int /*DWORD*/ dwForwardAge;
            internal int /*DWORD*/ dwForwardNextHopAS;
            internal int /*DWORD*/ dwForwardMetric1;
            internal int /*DWORD*/ dwForwardMetric2;
            internal int /*DWORD*/ dwForwardMetric3;
            internal int /*DWORD*/ dwForwardMetric4;
            internal int /*DWORD*/ dwForwardMetric5;
        };
    
        [DllImport("iphlpapi", CharSet = CharSet.Auto)]
        private extern static int GetIpForwardTable(IntPtr /*PMIB_IPFORWARDTABLE*/ pIpForwardTable, ref int /*PULONG*/ pdwSize, bool bOrder);
    
        [DllImport("iphlpapi", CharSet = CharSet.Auto)]
        private extern static int CreateIpForwardEntry(IntPtr /*PMIB_IPFORWARDROW*/ pRoute);
    
        static void Main(string[] args)
        {
            var fwdTable = IntPtr.Zero;
            int size = 0;
    
            var result = GetIpForwardTable(fwdTable, ref size, true);
            fwdTable = Marshal.AllocHGlobal(size);
    
            result = GetIpForwardTable(fwdTable, ref size, true);
            var str = (IPForwardTable)Marshal.PtrToStructure(fwdTable, typeof(IPForwardTable));
    
            IPFORWARDROW[] table = new IPFORWARDROW[str.Size];
            IntPtr p = fwdTable + Marshal.SizeOf(str.Size);
            for (int i = 0; i < str.Size; ++i)
            {
                table[i] = (IPFORWARDROW)Marshal.PtrToStructure(p, typeof(IPFORWARDROW));
                p += Marshal.SizeOf(typeof(IPFORWARDROW));
            }
    
    
            Marshal.FreeHGlobal(fwdTable);
        }