1. Summary
Usually for an executing process, we will focus on the process's memory/CPU occupation, network connection, startup parameters, image path, thread, stack and other information.
This information can be easily obtained through methods such as task managers, command lines, etc. But where does this information come from?
- linux
Since the Linux platform does not directly provide an access interface for process information, it displays relevant business information to users through the /proc file system. Although it is also possible to get the information we want directly from the kernel through neklink communication, the encoding complexity is relatively high, so we chose to use the /proc file system interface. - Window
The Windows platform natively provides a large number of operating system-related interfaces, but the functions of each interface are relatively independent, so you need to find the corresponding interface to query the data and finally assemble the data.
2. Process list
1)linux
/proc/[PID]/
: This is a corresponding directory for each running process, where[PID]
It is the ID number of the process.
DIR *proc = opendir("/proc");
if (proc) {
struct dirent *entry;
while ((entry = readdir(proc)) != NULL) {
if (entry->d_type == DT_DIR) {
std::string dir_name(entry->d_name);
if (dir_name.find_first_not_of("0123456789") == std::string::npos) {
int32_t pid = atoi(entry->d_name);
}
}
}
closedir(proc);
}
2)Window
There are many ways to get a process list on the Windows platform, just choose any one of them.
- CreateToolhelp32Snapshot is used in combination with Process32First, etc.;
- EnumProcesses enums all process IDs;
- NtQuerySystemInformation query SystemProcessInformation information
- ……
Here, the first method is used to traverse the process.
Of course, since the CreateToolhelp32Snapshot underlying layer is actually also implemented through NtQuerySystemInformation. Therefore, if you pursue high efficiency, you can use the third solution.
HANDLE hSnapshot =
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
if (hSnapshot) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
if (Process32First(hSnapshot, &pe32)) {
do {
std::string path;
W2A(pe32.szExeFile, &path);
Proc detail_info;
detail_info.pid = pe32.th32ProcessID;
detail_info.name = path;
// GetDetailInfo
proc_map->emplace(pe32.th32ProcessID, detail_info);
} while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
}
A third solution is also attached:
typedef NTSTATUS(NTAPI *P_NT_QUERY_SYSTEM_INFORMATION)(
SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation,
ULONG SystemInformationLength, PULONG ReturnLength);
HMODULE hNtdll = GetModuleHandle(L"");
P_NT_QUERY_SYSTEM_INFORMATION pfnNtQueryInformationProcess = NULL;
if (hNtdll) {
pfnNtQueryInformationProcess =
reinterpret_cast<P_NT_QUERY_SYSTEM_INFORMATION>(
GetProcAddress(hNtdll, "NtQuerySystemInformation"));
ULONG length = 0;
PUCHAR pInfo = NULL;
do {
DWORD result = pfnNtQueryInformationProcess(SystemProcessInformation, pInfo,
length, &length);
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
if (result != 0) {
if (result == STATUS_INFO_LENGTH_MISMATCH) {
pInfo = new UCHAR[length];
continue;
}
break;
}
PSYSTEM_PROCESS_INFORMATION _ProcessInfo;
ULONG Offset = 0;
do {
_ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&pInfo[Offset];
int64_t pid = HandleToLong(_ProcessInfo->UniqueProcessId);
// ToDo
Offset += _ProcessInfo->NextEntryOffset;
} while (_ProcessInfo->NextEntryOffset);
break;
} while (true);
if (pInfo) {
delete pInfo;
}
}
For the specific structure of SYSTEM_THREAD_INFORMATION, please refer to:SYSTEM_PROCESS_INFORMATION
3. Parameter list
1)linux
/proc/[PID]/cmdline
: This file contains command line parameters to start the process. Parameters are separated by null characters ('\0').
char cmd_path[PROCPATHLEN];
sprintf(cmd_path, "/proc/%d/cmdline", pid);
FILE *fp = fopen(cmd_path, "r");
if (fp) {
char line[4096];
if (fgets(line, sizeof(line), fp)) {
imagepath->assign(line);
startparamater->assign(line);
int32_t offset = startparamater->size() + 1;
while (line[offset]) {
startparamater->append(" ");
startparamater->append(line + offset);
offset += strlen(line + offset) + 1;
}
}
fclose(fp);
}
2)Window
The Windows platform does not directly provide an interface to obtain process startup parameters, but information can be obtained by parsing the PEB (process environment block) address of the process.
typedef NTSTATUS(NTAPI *NT_QUERY_INFORMATION_PROCESS)(HANDLE, PROCESSINFOCLASS,
PVOID, ULONG, PULONG);
HMODULE hNtdll = GetModuleHandle(L"Ntdll");
if (hNtdll) {
NT_QUERY_INFORMATION_PROCESS pNtQueryInformationProcess =
(NT_QUERY_INFORMATION_PROCESS)GetProcAddress(hNtdll,
"NtQueryInformationProcess");
if (pNtQueryInformationProcess) {
// Read-only open process handle
HANDLE hProcess =
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (hProcess) {
PWSTR buffer = NULL;
do {
PROCESS_BASIC_INFORMATION pbi = {0};
// Read the basic process information RTL_USER_PROCESS_PARAMETERS
if (pNtQueryInformationProcess(
hProcess, ProcessBasicInformation, (PVOID)&pbi,
sizeof(PROCESS_BASIC_INFORMATION), NULL)) {
break;
}
if (NULL == pbi.PebBaseAddress) {
break;
}
PEB peb;
SIZE_T dwDummy;
// Read the PEB structure from the PEB address
if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb),
&dwDummy)) {
break;
}
RTL_USER_PROCESS_PARAMETERS para;
// Read the RTL_USER_PROCESS_PARAMETERS structure from the parameter structure address
if (!ReadProcessMemory(hProcess, peb.ProcessParameters, ¶,
sizeof(para), &dwDummy)) {
break;
}
// Read the image file path from the image file address
LPVOID lpAddress = para.ImagePathName.Buffer;
DWORD dwSize = para.ImagePathName.Length;
buffer = new WCHAR[dwSize / sizeof(WCHAR) + 1];
buffer[dwSize / sizeof(WCHAR)] = 0x00;
if (!ReadProcessMemory(hProcess, lpAddress, buffer, dwSize, &dwDummy)) {
break;
}
W2A(buffer, imagepath);
delete[] buffer;
buffer = NULL;
// Read the parameter list from the parameter list address
lpAddress = para.CommandLine.Buffer;
dwSize = para.CommandLine.Length;
buffer = new WCHAR[dwSize / sizeof(WCHAR) + 1];
buffer[dwSize / sizeof(WCHAR)] = 0x00;
if (!ReadProcessMemory(hProcess, lpAddress, buffer, dwSize, &dwDummy))
break;
W2A(buffer, startparamater);
delete[] buffer;
buffer = NULL;
result = true;
} while (false);
if (buffer) {
delete[] buffer;
}
CloseHandle(hProcess);
}
}
}
4. Dynamic library
1)linux
/proc/[PID]/maps
: This file contains the memory mapping information of the process, displaying the memory address range used by the process and its corresponding permissions.
char map_path[PROCPATHLEN];
sprintf(map_path, "/proc/%d/maps", pid);
FILE *fp = fopen(map_path, "r");
if (fp) {
char line[1024];
char filename[1024];
std::unordered_set<std::string> module_sets;
while (fgets(line, sizeof(line), fp)) {
sscanf(line, "%*s %*s %*s %*s %*ld %s", filename);
if (filename[0] == '/') {
module_sets.emplace(filename);
}
}
fclose(fp);
for (std::unordered_set<std::string>::const_iterator itr =
module_sets.begin();
itr != module_sets.end(); itr++) {
ptable->push_back(*itr);
}
}
2)Window
The Windows platform can directly use the Module32First family function to traverse all modules loaded by processes.
HANDLE hSnapshot =
CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
if (hSnapshot) {
MODULEENTRY32 md32;
md32.dwSize = sizeof(md32);
if (Module32First(hSnapshot, &md32)) {
do {
std::string filepath;
W2A(md32.szExePath, &filepath);
ptable->push_back(filepath);
} while (Module32Next(hSnapshot, &md32));
}
CloseHandle(hSnapshot);
}
5. CPU utilization
1)linux
The CPU utilization of the process cannot be obtained directly from the system, but needs to be calculated through the time slice algorithm. Here, we refer totop commandImplement simple process CPU utilization calculation.
algorithm:
- Get the kernel frequency Hertz;
hertz_ = sysconf(_SC_CLK_TCK)
- Read the system running time and calculate the difference between the last running time et;
FILE *fp = fopen("/proc/uptime", "r");
if (fp) {
char line[1024];
fgets(line, sizeof(line), fp);
fclose(fp);
sscanf(line, "%lf", uptime);
}
- Calculate refresh frequency Frame_etscale
float et = uptime_cur - uptime_save_;
if (et < 0.01) {
et = 0.005;
}
uptime_save_ = uptime_cur;
frame_etscale_ = 100.0f / ((float)hertz_ * (float)et * 1);
- traversing the user state u_time and kernel state time s_time of the read process;
char stat_path[PROCPATHLEN];
sprintf(stat_path, "/proc/%d/stat", pid);
FILE *fp = fopen(stat_path, "r");
if (fp) {
char line[1024];
if (fgets(line, sizeof(line), fp)) {
int64_t u_time, s_time, wait_u_time, wait_s_time, start_time;
sscanf(line,
"%*d %*s %*c %*d %*d %*d %*d %*d "
"%*lu %*lu %*lu %*lu %*lu"
"%llu %llu %llu %llu"
"%*ld %*ld "
"%*d "
"%*ld "
"%llu ", /* start_time */
&u_time, &s_time, &wait_u_time, &wait_s_time, &start_time);
cpu_time->s_time = s_time;
cpu_time->u_time = u_time;
cpu_time->start_time = start_time;
}
fclose(fp);
}
- Time slice
- Repeat steps 2, 3, and 4 to calculate the CPU utilization of the process cpu_usage;
tics = process.new_time - process.old_time;
cpu_usage = tics * etscale_;
2)Window
The algorithm of the Windows platform is similar to Linux.
algorithm:
- Get the current CPU time
FILETIME idleTime, kernelTime, userTime;
if (!GetSystemTimes(&idleTime, &kernelTime, &userTime)) {
return;
}
ULARGE_INTEGER cpu_time;
cpu_time.LowPart = kernelTime.dwLowDateTime + userTime.dwLowDateTime;
cpu_time.HighPart = kernelTime.dwHighDateTime + userTime.dwHighDateTime;
time = cpu_time.QuadPart;
- Time to traverse all processes
HANDLE hProcess =
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (hProcess) {
FILETIME start_time, exit_time, s_time, u_time;
if (!GetProcessTimes(hProcess, &start_time, &exit_time, &s_time, &u_time)) {
return false;
}
FileTimeToInt64(s_time, cpu_time->s_time);
FileTimeToInt64(u_time, cpu_time->u_time);
FileTimeToInt64(start_time, cpu_time->start_time);
CloseHandle(hProcess);
}
- Time slice
- Repeat steps 1 and 2 to calculate the CPU utilization of the process cpu_usage;
*cpu_usage = (process.new_time - process.old_time) * 1000 /
(system_time_.new_time - system_time_.old_time);
6. Memory usage
1)linux
/proc/[PID]/status
: This file contains various information about the process status, such as process ID, parent process ID, running status, memory usage, etc.
// Get the total amount of physical memory, unit Byte
const char *meminfo_path = "/proc/meminfo";
FILE *fp = fopen(meminfo_path, "r");
if (fp) {
char line[4096];
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "MemTotal:", 9) == 0) {
sscanf(line, "%*s:%d", sys_mem_size_);
break;
}
}
fclose(fp);
}
//Get the physical memory usage of the process, unit KB
char status_path[PROCPATHLEN];
sprintf(status_path, "/proc/%d/status", pid);
FILE *fp = fopen(status_path, "r");
if (fp) {
char line[4096];
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "VmRSS:", 6) == 0) {
sscanf(line, "%*s:%d", mem_used_size);
break;
}
}
fclose(fp);
}
2)Window
MEMORYSTATUSEX mem_info;
mem_info.dwLength = sizeof(mem_info);
// Get the total amount of physical memory, unit Byte
if (GlobalMemoryStatusEx(&mem_info)) {
sys_mem_size_ = mem_info.ullTotalPhys;
}
SYSTEM_INFO si;
GetSystemInfo(&si);
HANDLE hProcess =
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (hProcess) {
PSAPI_WORKING_SET_INFORMATION workset_info = NULL;
// Query process memory tool set
if (!QueryWorkingSet(hProcess, &workset_info, sizeof(workset_info))) {
if (GetLastError() == ERROR_BAD_LENGTH) {
// The memory required for calculation is that since the tool set length changes dynamically, 64 tool set spaces are applied here.
size_t length =
sizeof(PSAPI_WORKING_SET_INFORMATION) +
sizeof(PSAPI_WORKING_SET_BLOCK) * (workset_info.NumberOfEntries + 64);
PPSAPI_WORKING_SET_INFORMATION p_workset_info =
(PPSAPI_WORKING_SET_INFORMATION) new char[length];
if (QueryWorkingSet(hProcess, p_workset_info, length)) {
*mem_used_size = 0;
for (int i = 0; i < p_workset_info->NumberOfEntries; i++) {
// Determine whether the tool set is shared
if (p_workset_info->WorkingSetInfo[i].Flags &&
p_workset_info->WorkingSetInfo[i].Shared == 0) {
*mem_used_size += si.dwPageSize;
}
}
}
delete[] (char *)p_workset_info;
} else {
return false;
}
}
// Byte units convert KB units
*mem_used_size = (*mem_used_size / 1024);
return true;
}
7. Username
1)linux
/proc/[PID]/status
char status_path[PROCPATHLEN];
sprintf(status_path, "/proc/%d/status", pid);
FILE *fp = fopen(status_path, "r");
if (fp) {
char line[4096];
while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "Uid:", 4) == 0) {
int32_t uid = 0;
sscanf(line, "%*s:%d", &uid);
struct passwd *_passwd;
_passwd = getpwuid(uid);
if (_passwd) {
username = _passwd->pw_name;
}
}
}
fclose(fp);
}
2)Window
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (handle != 0x00) {
HANDLE token;
// Open the process token
if (OpenProcessToken(handle, TOKEN_QUERY, &token)) {
DWORD token_size = 0;
PTOKEN_USER p_token_user = NULL;
SID_NAME_USE sn;
// Get user token from process token
if (!GetTokenInformation(token, TokenUser, p_token_user, token_size,
&token_size)) {
if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
p_token_user = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, token_size);
if (p_token_user) {
if (GetTokenInformation(token, TokenUser, p_token_user, token_size,
&token_size)) {
TCHAR szUserName[MAX_PATH] = {0};
DWORD dwUserSize = MAX_PATH;
TCHAR szDomain[MAX_PATH] = {0};
DWORD dwDomainSize = MAX_PATH;
// Query user name based on user token
if (LookupAccountSid(NULL, ((PTOKEN_USER)p_token_user)->User.Sid,
szUserName, &dwUserSize, szDomain,
&dwDomainSize, &sn)) {
W2A(szUserName, username);
ret = true;
}
}
HeapFree(GetProcessHeap(), 0, p_token_user);
}
}
}
CloseHandle(token);
}
CloseHandle(handle);
}
8. Network connection
1)linux
/proc/net/tcp /proc/net/tcp6 /proc/net/udp /proc/net/udp6
: Provides detailed information about the current TCP /TCP6/UDP/UDP6 socket./proc/[PID]/fd/
: This is a folder containing the list of file descriptors currently opened by the process.
- First, parse socket information, including the socket's inode number;
const char *tcp_file[] = {"/proc/net/tcp", "/proc/net/tcp6"};
for (int i = 0; i < 2; i++) {
FILE *fp = fopen(tcp_file[i], "r");
if (!fp) {
return;
}
char line[1024];
while (fgets(line, sizeof(line), fp)) {
unsigned long rxq, txq, time_len, retr, inode;
int num, local_port, remote_port, d, state, uid, timer_run, timeout;
char rem_addr[128], local_addr[128];
// Analyze TCP information
num = sscanf(line,
"%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX "
"%X:%lX %lX %d %d %lu %*s\n",
&d, local_addr, &local_port, rem_addr, &remote_port, &state,
&txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout,
&inode);
if (num < 11) {
continue;
}
// ipv6 address
if (strlen(local_addr) > 8) {
char addr6[INET6_ADDRSTRLEN];
struct in6_addr in6;
sscanf(local_addr, "%08X%08X%08X%08X", &in6.s6_addr32[0],
&in6.s6_addr32[1], &in6.s6_addr32[2], &in6.s6_addr32[3]);
// Port sequence conversion
inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
sscanf(rem_addr, "%08X%08X%08X%08X", &in6.s6_addr32[0], &in6.s6_addr32[1],
&in6.s6_addr32[2], &in6.s6_addr32[3]);
inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
} else {
// ipv4 address
char addr[INET_ADDRSTRLEN];
struct in_addr in;
sscanf(local_addr, "%X", &in.s_addr);
inet_ntop(AF_INET, &in, addr, sizeof(addr));
sscanf(rem_addr, "%X", &in.s_addr);
inet_ntop(AF_INET, &in, addr, sizeof(addr));
}
}
fclose(fp);
}
const char *udp_file[] = {"/proc/net/udp", "/proc/net/udp6"};
for (int i = 0; i < 2; i++) {
FILE *fp = fopen(udp_file[i], "r");
if (!fp) {
return false;
}
char line[1024];
while (fgets(line, sizeof(line), fp)) {
unsigned long rxq, txq, time_len, retr, inode;
int num, local_port, remote_port, d, state, uid, timer_run, timeout;
char rem_addr[128], local_addr[128];
// Analyze UDP information
num = sscanf(line,
"%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX "
"%X:%lX %lX %d %d %lu %*s\n",
&d, local_addr, &local_port, rem_addr, &remote_port, &state,
&txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout,
&inode);
if (num < 10) {
continue;
}
if (strlen(local_addr) > 8) {
// ipv6 address
char addr6[INET6_ADDRSTRLEN];
struct in6_addr in6;
sscanf(local_addr, "%08X%08X%08X%08X", &in6.s6_addr32[0],
&in6.s6_addr32[1], &in6.s6_addr32[2], &in6.s6_addr32[3]);
inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
} else {
// ipv4 address
char addr[INET_ADDRSTRLEN];
struct in_addr in;
sscanf(local_addr, "%X", &in.s_addr);
inet_ntop(AF_INET, &in, addr, sizeof(addr));
}
}
fclose(fp);
}
- Iterate through the fd folders of all processes and match the inode number.
2)WIndows
Directly use GetExtendedTcpTable and GetExtendedUdpTable functions to query TCP and UDP information.
DWORD bufferSize;
MIB_TCPTABLE_OWNER_PID *net_table = NULL;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData)) {
return;
}
do {
SOCKADDR_IN v4 = {AF_INET};
SOCKADDR_IN6 v6 = {AF_INET6};
int32_t ip[] = {AF_INET6, AF_INET};
char ipaddress[INET6_ADDRSTRLEN];
DWORD ipaddress_length = 0;
for (int i = 0; i < 2; i++) {
bufferSize = 0;
if (GetExtendedTcpTable(NULL, &bufferSize, FALSE, ip[i],
TCP_TABLE_OWNER_PID_ALL,
0) == ERROR_INSUFFICIENT_BUFFER) {
BYTE *net_table = new BYTE[bufferSize];
if (net_table != NULL) {
if (GetExtendedTcpTable(net_table, &bufferSize, FALSE, ip[i],
TCP_TABLE_OWNER_PID_ALL, 0) == NO_ERROR) {
if (i == 0) {
MIB_TCP6TABLE_OWNER_PID *net_table_v6 =
(MIB_TCP6TABLE_OWNER_PID *)net_table;
for (DWORD i = 0; i < net_table_v6->dwNumEntries; i++) {
MIB_TCP6ROW_OWNER_PID row = net_table_v6->table[i];
// local addr info
ipaddress_length = INET6_ADDRSTRLEN;
memcpy(v6.sin6_addr.s6_addr, row.ucLocalAddr,
sizeof(row.ucLocalAddr));
if (!WSAAddressToStringA((LPSOCKADDR)&v6, sizeof(v6), NULL,
ipaddress, &ipaddress_length)) {
local_host = ipaddress;
}
local_port = ntohs(row.dwLocalPort);
// remote addr info
ipaddress_length = INET6_ADDRSTRLEN;
memcpy(v6.sin6_addr.s6_addr, row.ucRemoteAddr,
sizeof(row.ucRemoteAddr));
if (!WSAAddressToStringA((LPSOCKADDR)&v6, sizeof(v6), NULL,
ipaddress, &ipaddress_length)) {
remote_host = ipaddress;
}
remote_port = ntohs(row.dwRemotePort);
}
} else {
MIB_TCPTABLE_OWNER_PID *net_table_v4 =
(MIB_TCPTABLE_OWNER_PID *)net_table;
for (DWORD i = 0; i < net_table_v4->dwNumEntries; i++) {
MIB_TCPROW_OWNER_PID row = net_table_v4->table[i];
// local addr info
ipaddress_length = INET_ADDRSTRLEN;
v4.sin_addr.s_addr = row.dwLocalAddr;
if (!WSAAddressToStringA((LPSOCKADDR)&v4, sizeof(v4), NULL,
ipaddress, &ipaddress_length)) {
local_host = ipaddress;
}
local_port = ntohs(row.dwLocalPort);
// remote addr info
ipaddress_length = INET_ADDRSTRLEN;
v4.sin_addr.s_addr = row.dwRemoteAddr;
if (!WSAAddressToStringA((LPSOCKADDR)&v4, sizeof(v4), NULL,
ipaddress, &ipaddress_length)) {
remote_host = ipaddress;
}
remote_port = ntohs(row.dwRemotePort);
}
}
}
delete[] net_table;
}
}
}
for (int i = 0; i < 2; i++) {
bufferSize = 0;
if (GetExtendedUdpTable(NULL, &bufferSize, FALSE, ip[i],
UDP_TABLE_OWNER_PID,
0) == ERROR_INSUFFICIENT_BUFFER) {
BYTE *net_table = new BYTE[bufferSize];
if (net_table != NULL) {
if (GetExtendedUdpTable(net_table, &bufferSize, FALSE, ip[i],
UDP_TABLE_OWNER_PID, 0) == NO_ERROR) {
if (i == 0) {
MIB_UDP6TABLE_OWNER_PID *net_table_v6 =
(MIB_UDP6TABLE_OWNER_PID *)net_table;
for (DWORD i = 0; i < net_table_v6->dwNumEntries; i++) {
MIB_UDP6ROW_OWNER_PID row = net_table_v6->table[i];
// local addr netInfo
ipaddress_length = INET6_ADDRSTRLEN;
memcpy(v6.sin6_addr.s6_addr, row.ucLocalAddr,
sizeof(row.ucLocalAddr));
if (!WSAAddressToStringA((LPSOCKADDR)&v6, sizeof(v6), NULL,
ipaddress, &ipaddress_length)) {
local_host = ipaddress;
}
local_port = ntohs(row.dwLocalPort);
}
} else {
MIB_UDPTABLE_OWNER_PID *net_table_v4 =
(MIB_UDPTABLE_OWNER_PID *)net_table;
for (DWORD i = 0; i < net_table_v4->dwNumEntries; i++) {
MIB_UDPROW_OWNER_PID row = net_table_v4->table[i];
// local addr netInfo
ipaddress_length = INET_ADDRSTRLEN;
v4.sin_addr.s_addr = row.dwLocalAddr;
if (!WSAAddressToStringA((LPSOCKADDR)&v4, sizeof(v4), NULL,
ipaddress, &ipaddress_length)) {
local_host = ipaddress;
}
local_port = ntohs(row.dwLocalPort);
}
}
}
delete[] net_table;
}
}
}
} while (false);
WSACleanup();
9. To be continued
……