Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to monitor process network usage under windows

Tags:

c++

windows

mfc

I am looking for a way to get the list of information as the Resource Monitor under windows did, such as: enter image description here I want to know the address and the amount of send and receive usage of a selected application.

At the beginning I am looking for a C++ win32 api or other open source library to do this, I can find something like GetProcessInformation but it does not include network information.

I saw some similar topics but they didn't help.
Ref_001, It seems the network monitor api cannot do application specific monitor.
Ref_002, I am not sure if OpenTrace/ProcessTrace/StopTrace can get me the network usage or not and also I am not sure how to use it.
Ref_003, They are suggesting some tools but it is not what I want.

like image 520
sflee Avatar asked Jun 22 '17 03:06

sflee


2 Answers

Windows supplies you with this information in two parts from different functions that you'll need to put together to get the full story. Well, technically, it's three functions: for the second part of the data, there are separate functions for IPv4 and IPv6 data.

The first function is GetExtendedTcpTable. To get all the information above, you'll probably need to call this (at least) twice: once with the TCP_TABLE_OWNER_PID_CONNECTIONS flag, and once with the TCP_TABLE_OWNER_MODULE_CONNECTIONS flag to retrieve both the PID and the module name of the local executable.

The second pair gets you statistics about the data sent/received on a particular connection. Each connection is identified by a combination of local address/port and remote address port (same as used above). You retrieve the information with GetPerTcpConnectionEStats for IPv4 or GetPerTcp6ConnectionEStats for IPv6.

Either of these will retrieve a table, with each row in the table containing statistics for one connection. If you have (for example) multiple tabs open in your browser, could choose to show the data for each connection individually, or you could amalgamate them as you saw fit.

like image 194
Jerry Coffin Avatar answered Oct 29 '22 13:10

Jerry Coffin


Thanks for your guidance Jerry Coffin

This is some very "POC" code I got working from these libraries. Spoiler alert - Just implemented TCPv4 here. Code referenced from

https://learn.microsoft.com/en-gb/windows/desktop/api/iphlpapi/nf-iphlpapi-getpertcpconnectionestats

Please forgive hacky nature of code, but I thought better to post than wait for that perfect day that will never arrive!

Results container "NetworkPerformanceItem.h"

#pragma once
#include "stdafx.h"
#include <string>
#include <windows.h>

class NetworkPerformanceItem
{
public:
    NetworkPerformanceItem();
    ~NetworkPerformanceItem();

    INT ProcessId;
    INT State;
    std::string LocalAddress;
    std::string RemoteAddress;
    int LocalPort;
    int RemotePort;
    LONG BytesOut;
    LONG BytesIn;
    LONG OutboundBandwidth;
    LONG InboundBandwidth;
    int Pass = 0;
    std::string CollectionTime;

};

Header "NetworkPerformanceScanner.h"

#pragma once
#include <iostream>
#include <vector>
#include <sstream>
#include "NetworkPerformanceItem.h"
#include <iomanip>
#include <iostream>
#include <windows.h>
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <iphlpapi.h>
#include <Tcpestats.h>
#include <stdlib.h>
#include <stdio.h>


using namespace std;

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")

#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) 
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

class NetworkPerformanceScanner
{
public:
    NetworkPerformanceScanner();
    ~NetworkPerformanceScanner();

    std::vector<NetworkPerformanceItem> ScanNetworkPerformance(int sampleId);
};

Source "NetworkPerformanceScanner.cpp"

#include "stdafx.h"
    #include "NetworkPerformanceScanner.h"



NetworkPerformanceScanner::NetworkPerformanceScanner()
{

}


NetworkPerformanceScanner::~NetworkPerformanceScanner()
{
}

// TODO - implement TCP v6, UDP
std::vector<NetworkPerformanceItem> NetworkPerformanceScanner::ScanNetworkPerformance(int pass)
{

    std::vector<unsigned char> buffer;
    DWORD dwSize = sizeof(MIB_TCPTABLE_OWNER_PID);
    DWORD dwRetValue = 0;
    vector<NetworkPerformanceItem> networkPerformanceItems;

    // get local computer time with timezone offset
    auto time = std::time(nullptr);
    std::ostringstream timeStream;
    timeStream << std::put_time(std::localtime(&time), "%F %T%z");
    string collectionTime = timeStream.str();

    // repeat till buffer is big enough
    do
    {
        buffer.resize(dwSize, 0);
        dwRetValue = GetExtendedTcpTable(buffer.data(), &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);

    } while (dwRetValue == ERROR_INSUFFICIENT_BUFFER);

    if (dwRetValue == ERROR_SUCCESS)
    {
        // good case

        // cast to access element values
        PMIB_TCPTABLE_OWNER_PID ptTable = reinterpret_cast<PMIB_TCPTABLE_OWNER_PID>(buffer.data());

        cout << "Number of Entries: " << ptTable->dwNumEntries << endl << endl;


        // caution: array starts with index 0, count starts by 1
        for (DWORD i = 0; i < ptTable->dwNumEntries; i++)
        {
            NetworkPerformanceItem networkPerformanceItem;

            networkPerformanceItem.ProcessId = ptTable->table[i].dwOwningPid;
            networkPerformanceItem.State = ptTable->table[i].dwState;           

            cout << "PID: " << ptTable->table[i].dwOwningPid << endl;
            cout << "State: " << ptTable->table[i].dwState << endl;

            std::ostringstream localStream;
            localStream << (ptTable->table[i].dwLocalAddr & 0xFF)
                << "."
                << ((ptTable->table[i].dwLocalAddr >> 8) & 0xFF)
                << "."
                << ((ptTable->table[i].dwLocalAddr >> 16) & 0xFF)
                << "."
                << ((ptTable->table[i].dwLocalAddr >> 24) & 0xFF)
                << ":"
                << htons((unsigned short)ptTable->table[i].dwLocalPort);
            networkPerformanceItem.LocalAddress = localStream.str();
            networkPerformanceItem.LocalPort = ptTable->table[i].dwLocalPort;

            std::ostringstream remoteStream;
            remoteStream << (ptTable->table[i].dwRemoteAddr & 0xFF)
                << "."
                << ((ptTable->table[i].dwRemoteAddr >> 8) & 0xFF)
                << "."
                << ((ptTable->table[i].dwRemoteAddr >> 16) & 0xFF)
                << "."
                << ((ptTable->table[i].dwRemoteAddr >> 24) & 0xFF)
                << ":"
                << htons((unsigned short)ptTable->table[i].dwRemotePort);
            networkPerformanceItem.RemoteAddress = remoteStream.str();
            networkPerformanceItem.RemotePort = ptTable->table[i].dwRemotePort;

            MIB_TCPROW row;
            row.dwLocalAddr = ptTable->table[i].dwLocalAddr;
            row.dwLocalPort = ptTable->table[i].dwLocalPort;
            row.dwRemoteAddr = ptTable->table[i].dwRemoteAddr;
            row.dwRemotePort = ptTable->table[i].dwRemotePort;
            row.dwState = ptTable->table[i].dwState;
            void *processRow = &row;

            if (row.dwRemoteAddr != 0)
            {
                ULONG rosSize = 0, rodSize = 0;
                ULONG winStatus;
                PUCHAR ros = NULL, rod = NULL;
                rodSize = sizeof(TCP_ESTATS_DATA_ROD_v0);
                PTCP_ESTATS_DATA_ROD_v0 dataRod = { 0 };

                if (rosSize != 0) {
                    ros = (PUCHAR)malloc(rosSize);
                    if (ros == NULL) {
                        wprintf(L"\nOut of memory");
                        return networkPerformanceItems;
                    }
                    else
                        memset(ros, 0, rosSize); // zero the buffer
                }
                if (rodSize != 0) {
                    rod = (PUCHAR)malloc(rodSize);
                    if (rod == NULL) {
                        free(ros);
                        wprintf(L"\nOut of memory");
                        return networkPerformanceItems;
                    }
                    else
                        memset(rod, 0, rodSize); // zero the buffer
                }

                winStatus = GetPerTcpConnectionEStats((PMIB_TCPROW)&row, TcpConnectionEstatsData, NULL, 0, 0, ros, 0, rosSize, rod, 0, rodSize);

                dataRod = (PTCP_ESTATS_DATA_ROD_v0)rod;

                networkPerformanceItem.BytesIn = dataRod->DataBytesIn;
                networkPerformanceItem.BytesOut = dataRod->DataBytesOut;

                PTCP_ESTATS_BANDWIDTH_ROD_v0 bandwidthRod = { 0 };

                rodSize = sizeof(TCP_ESTATS_BANDWIDTH_ROD_v0);
                if (rodSize != 0) {
                    rod = (PUCHAR)malloc(rodSize);
                    if (rod == NULL) {
                        free(ros);
                        wprintf(L"\nOut of memory");
                        return networkPerformanceItems;
                    }
                    else
                        memset(rod, 0, rodSize); // zero the buffer
                }

                winStatus = GetPerTcpConnectionEStats((PMIB_TCPROW)&row,TcpConnectionEstatsBandwidth, NULL, 0, 0, ros, 0, rosSize, rod, 0, rodSize);

                bandwidthRod = (PTCP_ESTATS_BANDWIDTH_ROD_v0)rod;
                networkPerformanceItem.OutboundBandwidth = bandwidthRod->OutboundBandwidth;
                networkPerformanceItem.InboundBandwidth = bandwidthRod->InboundBandwidth;

            }
            networkPerformanceItem.Pass = pass;
            networkPerformanceItem.CollectionTime = collectionTime;
            networkPerformanceItems.push_back(networkPerformanceItem);
        }
    }
    else
    {
        // bad case, do some sh*t here
    }

    return networkPerformanceItems;
}
like image 3
Malcolm Swaine Avatar answered Oct 29 '22 15:10

Malcolm Swaine