我试图从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);
}
}
比较这个和非托管版本之间的内存(运行正常),我确实看到了我无法解释的封送结构内存的一些差异;有几个字节不同。
非常感谢任何帮助!
答案 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的倍数的偏移处。因此,在dwNumEntries
和table
之间填充。你需要允许填充。
所以,在这一点上:
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"));