Since P/Invoke does not support returning dynamic sized arrays (you must statically specify the size of the array at compile time), I decided to write a C++/CLI wrapper for some functions I need in a .net application that is otherwise written in C#.
Take the GetTcpTable2 function from IpHlpApi.dll... I made C# classes to match the types in that function as follows:
public class MibTcpTable2
{
public int NumEntries;
public MibTcpRow2[] Table;
}
public class MibTcpRow2
{
public int State;
public int LocalAddr;
public int LocalPort;
public int RemoteAddr;
public int RemotePort;
public int OwningPid;
public int OffloadState;
}
In my C++/CLI program, I call GetTcpTable2 as shown in the MSDN example, and then iterate through the resulting array and assign its output to a new instance of the TcpTable2 class I made in C#. See code:
PMIB_TCPTABLE2 pTcpTable;
ULONG ulSize = 0;
DWORD dwRetVal = 0;
pTcpTable = (MIB_TCPTABLE2 *)MALLOC(sizeof(MIB_TCPTABLE2));
if (pTcpTable == NULL) {
return nullptr;
}
ulSize = sizeof(MIB_TCPTABLE);
if ((dwRetVal = ::GetTcpTable2(pTcpTable, &ulSize, TRUE)) == ERROR_INSUFFICIENT_BUFFER)
{
FREE(pTcpTable);
pTcpTable = (MIB_TCPTABLE2 *)MALLOC(ulSize);
if (pTcpTable == NULL) {
return nullptr;
}
}
NetClasses::MibTcpTable2^ managedTable = gcnew NetClasses::MibTcpTable2();
managedTable->Table = gcnew cli::array<NetClasses::MibTcpRow2^>(pTcpTable->dwNumEntries);
if ((dwRetVal = ::GetTcpTable2(pTcpTable, &ulSize, TRUE)) == NO_ERROR)
{
for (int i = 0; i < pTcpTable->dwNumEntries; i++)
{
managedTable->Table[i].LocalAddr = pTcpTable->table[i].dwLocalAddr;
managedTable->Table[i].LocalPort = pTcpTable->table[i].dwLocalPort;
managedTable->Table[i].OffloadState = pTcpTable->table[i].dwOffloadState;
managedTable->Table[i].OwningPid = pTcpTable->table[i].dwOwningPid;
managedTable->Table[i].RemoteAddr = pTcpTable->table[i].dwRemoteAddr;
managedTable->Table[i].RemotePort = pTcpTable->table[i].dwRemotePort;
managedTable->Table[i].State = pTcpTable->table[i].dwState;
}
}
However, Visual Studio 2015 hates the accesses to managedTable inside the for loop. It complains that "expression must have a class type." Ok, so that usually means you're using the wrong data accessor operator, so I tried a dot instead. No dice.
How the heck do I access the Table member of managedTable? The access to it before the for loop was valid. Why isn't it valid inside the for loop?
System.Net.IPEndPointinstead of twoint, for each address/port pair