具有Struct成员数组的编组结构

时间:2014-02-10 20:52:29

标签: c# pinvoke

我试图从P / Invoked'编组MIB_TCPTABLE_OWNER_MODULE结构。调用在iphlpapi.dll中定义的GetExtendedTcpTable。

我的P / Invoke签名定义如下:

[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwSize, bool sort, int ipVersion, int tableClass, int reserved);

根据MSDN上的文档(并查看头文件),这应该将pTcpTable参数设置为MIB_TCPTABLE_OWNER_MODULE结构的地址,该结构的成员是MIB_TCPROW_OWNER_MODULE结构的数组。来自tcpmib.h:

typedef struct _MIB_TCPTABLE_OWNER_MODULE
{
    DWORD                   dwNumEntries;
    MIB_TCPROW_OWNER_MODULE table[ANY_SIZE];
} MIB_TCPTABLE_OWNER_MODULE, *PMIB_TCPTABLE_OWNER_MODULE;

ANY_SIZE定义为1。

这是我的问题;我已经用C#定义了MIB_TCPTABLE_OWNER_MODULE和MIB_TCPROW_OWNER_MODULE结构:

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
    public uint dwNumEntries;
    MIB_TCPROW_OWNER_MODULE table;
}

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPROW_OWNER_MODULE
{
    public uint dwState;
    public uint dwLocalAddr;
    public uint dwLocalPort;
    public uint dwRemoteAddr;
    public uint dwRemotePort;
    public uint dwOwningPid;
    public ulong liCreateTimestamp;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = TCPIP_OWNING_MODULE_SIZE)]
    public ulong[] OwningModuleInfo;
}

由于我在声明时不知道返回的MIB_TCPTABLE_OWNER_MODULE表成员的大小,我的计划是增加IntPtr并使用Marshal.PtrToStructure从表成员中提取每个数组成员。

对Marshal.PtrToStructure的调用返回(没有内存冲突异常),但我结束了struct成员中的垃圾值。这是我的完整代码:

[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwSize, bool sort, int ipVersion, int tableClass, int reserved);

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPROW_OWNER_MODULE
{
    public uint dwState;
    public uint dwLocalAddr;
    public uint dwLocalPort;
    public uint dwRemoteAddr;
    public uint dwRemotePort;
    public uint dwOwningPid;
    public ulong liCreateTimestamp;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = TCPIP_OWNING_MODULE_SIZE)]
    public ulong[] OwningModuleInfo;
}

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
    public uint dwNumEntries;
    MIB_TCPROW_OWNER_MODULE table;
}

private const int TCPIP_OWNING_MODULE_SIZE = 16;
private const int AF_INET = 2;
private const int TCP_TABLE_OWNER_MODULE_ALL = 8;

public static void GetConnectionDetails()
{
    var bufferSize = 0;
    var ret = GetExtendedTcpTable(IntPtr.Zero, ref bufferSize, true, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0);
    var tableBuffer = Marshal.AllocHGlobal(bufferSize);

    try
    {
        ret = GetExtendedTcpTable(tableBuffer, ref bufferSize, true, AF_INET, TCP_TABLE_OWNER_MODULE_ALL, 0);

        if (ret != 0)
            throw new Exception("Oh noes!");

        var convertedTable = (MIB_TCPTABLE_OWNER_MODULE)Marshal.PtrToStructure(tableBuffer, typeof (MIB_TCPTABLE_OWNER_MODULE));

        var finalTable = new MIB_TCPROW_OWNER_MODULE[convertedTable.dwNumEntries];

        var rowPtr = (IntPtr) ((long) tableBuffer + Marshal.SizeOf(convertedTable.dwNumEntries));
        for (int i = 0; i < convertedTable.dwNumEntries; i++)
        {
            var row = (MIB_TCPROW_OWNER_MODULE)Marshal.PtrToStructure(rowPtr, typeof (MIB_TCPROW_OWNER_MODULE));

            finalTable[i] = row;

            rowPtr = (IntPtr) ((long) rowPtr + Marshal.SizeOf(row)); // Move to the next entry
        }

        foreach (var entry in finalTable)
        {
            // do something with each entry
            Console.WriteLine(entry.dwState);
            Console.WriteLine(entry.dwRemoteAddr);
        }
    }
    finally
    {
        Marshal.FreeHGlobal(tableBuffer);
    }
}

比较这个和非托管版本之间的内存(运行正常),我确实看到了我无法解释的封送结构内存的一些差异;有几个字节不同。

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:3)

考虑这个结构:

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
    public uint dwNumEntries;
    MIB_TCPROW_OWNER_MODULE table;
}

您假设table的偏移量等于dwNumEntries的大小。但是你忘记了对齐。由于MIB_TCPROW_OWNER_MODULE中的最大类型是8字节宽,因此该类型具有8的对齐。这意味着为了使其对齐,它必须放置在8的倍数的偏移处。因此,在dwNumEntriestable之间填充。你需要允许填充。

所以,在这一点上:

var rowPtr = (IntPtr) ((long) tableBuffer + 
    Marshal.SizeOf(convertedTable.dwNumEntries));

您将4添加到tableBuffer中的地址。您实际上需要添加8。您应该使用Marshal.OffsetOf来计算偏移量:

var rowPtr = (IntPtr)((long)tableBuffer + 
    (long)Marshal.OffsetOf(typeof(MIB_TCPTABLE_OWNER_MODULE), "table"));