I'm trying to marshal the MIB_TCPTABLE_OWNER_MODULE struct from a P/Invoked' call into GetExtendedTcpTable, defined in iphlpapi.dll.
My P/Invoke signature is defined as this:
[DllImport("iphlpapi.dll", SetLastError = true)]
private static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwSize, bool sort, int ipVersion, int tableClass, int reserved);
According to the documentation on MSDN (and looking through the header files), this should set the pTcpTable parameter to the address of a MIB_TCPTABLE_OWNER_MODULE structure, who has a member which is an array of MIB_TCPROW_OWNER_MODULE structures. From 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 is defined to be 1.
Here is my problem; I've defined the MIB_TCPTABLE_OWNER_MODULE and MIB_TCPROW_OWNER_MODULE structs in C# like so:
[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;
}
Since I won't know the size of the returned MIB_TCPTABLE_OWNER_MODULE's table member at declaration, my plan was to increment the IntPtr and use Marshal.PtrToStructure to extract each array member from the table member.
The call to Marshal.PtrToStructure returns (no memory violation exceptions), but I wind up with garbage values in the struct members. Here is my complete code:
[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);
}
}
Comparing memory between this and an unmanaged version (that works properly), I do see some differences in the memory of the marshaled struct that I can't account for; there are a few bytes different.
Any assistance is most appreciated!