2014-02-10 103 views
1

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

我的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的调用返回(没有内存违例异常),但是我在结构成员中找到了垃圾值。这是我的完整代码:

[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); 
    } 
} 

比较这和非托管版本(即正常工作),我看到在编组结构,我不能占的内存一定的差异之间的内存;有几个字节不同。

任何帮助最受赞赏!

+0

它已经被.NET包装了。使用IPGlobalProperties.GetActiveTcpConnections()和GetActiveTcpListeners()。使用参考源查看他们是如何做到的。 –

+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)); 

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

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

Gah,解释了我在内存转储中看到的dwNumEntries之后的字节差异。谢谢! –