0

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?

4
  • 1
    You might want to consider using System.Net.IPEndPoint instead of two int, for each address/port pair Commented Oct 5, 2015 at 4:09
  • Not a bad thought. Thanks for the suggestion. Commented Oct 5, 2015 at 5:41
  • Be careful to avoid helping too much, the .NET IPGlobalProperties.GetActiveTcpConnections() and GetActiveTcpListeners() methods already do this. Commented Oct 6, 2015 at 15:08
  • Cool. Thanks for that. I was unaware of that class. Those two in conjunction with each other are roughly equivalent to GetTcpTable. What I wrote above is equivalent to GetTcpTable2, as it includes the offload state. Commented Oct 7, 2015 at 18:02

1 Answer 1

2

Your array access is giving you a handle to a managed object, so shouldn't your field access also be -> rather than . ?

Array[i]->Field

Sign up to request clarification or add additional context in comments.

2 Comments

Wow. Figures that I was completely overlooking the blatantly obvious. Thanks.
No problem, glad I could help. Happens to all of us.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.