C# 使用结构成员数组封送结构

C# 使用结构成员数组封送结构,c#,pinvoke,C#,Pinvoke,我试图将MIB_TCPTABLE_OWNER_MODULE结构从p/Invoked调用封送到iphlapi.dll中定义的GetExtendedTcpTable 我的p/Invoke签名定义如下: [DllImport("iphlpapi.dll", SetLastError = true)] private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwSize, bool sort, int ipVers

我试图将MIB_TCPTABLE_OWNER_MODULE结构从p/Invoked调用封送到iphlapi.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_模块结构的地址,该结构的成员是MIB_TCPROW_OWNER_模块结构的数组。从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;
任何_大小都定义为1

这是我的问题;我在C中定义了MIB_TCPTABLE_OWNER_模块和MIB_TCPROW_OWNER_模块结构,如下所示:

[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_模块的表成员的大小,所以我的计划是增加IntPtr并使用Marshal.PtrToStructure从表成员中提取每个数组成员

调用Marshal.PtrToStructure不会返回内存冲突异常,但我在结构成员中得到了垃圾值。这是我的完整代码:

[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);
    }
}
比较这一版本和正常工作的非托管版本之间的内存,我确实看到封送结构的内存存在一些我无法解释的差异;有几个字节不同

非常感谢您的帮助

考虑一下这个结构:

[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MIB_TCPTABLE_OWNER_MODULE
{
    public uint dwNumEntries;
    MIB_TCPROW_OWNER_MODULE table;
}
假设表的偏移量等于dwNumEntries的大小。但是你忘记了对齐。由于MIB_TCPROW_OWNER_模块中的最大类型为8字节宽,因此该类型的对齐方式为8。这意味着为了使其对齐,必须将其放置在偏移量为8的倍数处。因此,在dwNumEntries和table之间存在填充。你需要考虑到这种填充

因此,在这一点上:

var rowPtr = (IntPtr) ((long) tableBuffer + 
    Marshal.SizeOf(convertedTable.dwNumEntries));
在tableBuffer中保存的地址中添加4。你实际上需要加8。应使用Marshal.OffsetOf计算偏移量:

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

它已经被.NET包起来了。使用IPGlobalProperties.GetActiveTcpConnections和GetActiveTcpListeners。使用参考源查看他们是如何做到这一点的。谢谢Hans!我不知道它已经包装好了;我去看看。仍然很好奇为什么我的封送代码没有按我预期的方式工作。Gah,这解释了我在内存转储中看到的dwnumeries之后的字节差异。谢谢