C# 使用结构成员数组封送结构
我试图将MIB_TCPTABLE_OWNER_MODULE结构从p/Invoked调用封送到iphlapi.dll中定义的GetExtendedTcpTable 我的p/Invoke签名定义如下: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
[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之后的字节差异。谢谢