C++ 获取进程信息

1. 概要

通常对于一个正在执行的进程而言,我们会关注进程的内存/CPU占用,网络连接,启动参数,映像路径,线程,堆栈等信息。
而通过类似任务管理器,命令行等方式可以轻松获取到这些信息。但是,这些信息究竟是从何而来呢?

  • linux
    由于linux平台未直接提供进程信息的访问接口,而是通过/proc文件系统向用户展示相关业务信息。虽然也可以通过neklink通信直接从内核获取我们想要的信息,编码复杂性相对较高,因此我们选择使用/proc文件系统的接口。
  • Window
    windows平台原生提供了大量操作系统相关的接口,但是每个接口的功能相对独立,因此需要找到对应的接口查询数据,最后将数据组装起来。

2. 进程列表

1)linux

/proc/[PID]/:这是每个正在运行的进程都有一个对应的目录,其中[PID]是进程的ID号。

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

windows平台获取进程列表的方式比较多,只需从中选择任意一种即可。

  • CreateToolhelp32Snapshot与Process32First等结合使用;
  • EnumProcesses枚举所有进程ID;
  • NtQuerySystemInformation查询SystemProcessInformation信息
  • ……

在这里,采用第一种方式遍历进程。
当然,由于CreateToolhelp32Snapshot底层实际上同样是通过NtQuerySystemInformation实现的。因此,若追求高效率的话,可以使用第三种方案。

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);
}

另附第三种方案实现:

typedef NTSTATUS(NTAPI *P_NT_QUERY_SYSTEM_INFORMATION)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation,
    ULONG SystemInformationLength, PULONG ReturnLength);

HMODULE hNtdll = GetModuleHandle(L"ntdll.dll");
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;
  }
}

SYSTEM_THREAD_INFORMATION具体结构请参考:SYSTEM_PROCESS_INFORMATION

3. 参数列表

1)linux

/proc/[PID]/cmdline:这个文件包含了启动该进程的命令行参数。参数之间使用null字符(‘\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

windows平台没有直接提供获取进程启动参数的接口,但是可以通过解析进程的PEB(进程环境块)地址,获取信息。

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) {
    // 只读打开进程句柄
    HANDLE hProcess =
        OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (hProcess) {
      PWSTR buffer = NULL;
      do {
        PROCESS_BASIC_INFORMATION pbi = {0};
        // 读取进程基本信息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;
        // 从PEB地址中读取PEB结构
        if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb),
                               &dwDummy)) {
          break;
        }

        RTL_USER_PROCESS_PARAMETERS para;
        // 从参数结构地址读取RTL_USER_PROCESS_PARAMETERS结构
        if (!ReadProcessMemory(hProcess, peb.ProcessParameters, &para,
                               sizeof(para), &dwDummy)) {
          break;
        }

        // 从映像文件地址读取映像文件路径
        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;

        // 从参数列表地址读取参数列表
        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. 动态库

1)linux

/proc/[PID]/maps:这个文件包含了进程的内存映射信息,显示了进程所使用的内存地址范围及其对应的权限。

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

windows平台可以直接使用Module32First族函数遍历所有进程加载的模块。

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利用率

1)linux

进程的Cpu利用率无法直接从系统获取,而需要通过时间片算法来计算。在这里,我们参考top命令实现简单的进程Cpu利用率计算。

算法:

  1. 获取内核频率Hertz;
   hertz_ = sysconf(_SC_CLK_TCK)
  1. 读取系统运行时间,计算与上次的运行时间的差值et;
   FILE *fp = fopen("/proc/uptime", "r");
   if (fp) {
     char line[1024];
     fgets(line, sizeof(line), fp);
     fclose(fp);
     sscanf(line, "%lf", uptime);
   }
  1. 计算刷新频率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);
  1. 遍历读取进程的用户态u_time和内核态时间s_time;
  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);
  }
  1. 时间切片
  2. 重复步骤2、3、4,计算进程的CPU利用率cpu_usage ;
   tics = process.new_time - process.old_time;
   cpu_usage = tics * etscale_;

2)Window

windows平台的算法与Linux类似。
算法:

  1. 获取当前CPU时间
   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;
  1. 遍历所有进程的时间
  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);
  }
  1. 时间切片
  2. 重复步骤1、2,计算进程的CPU利用率cpu_usage ;
   *cpu_usage = (process.new_time - process.old_time) * 1000 /
                            (system_time_.new_time - system_time_.old_time);

6. 内存占用

1)linux

/proc/[PID]/status:这个文件包含了有关进程状态的各种信息,如进程ID、父进程ID、运行状态、内存使用情况等。

// 获取物理内存总量,单位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);
}

//获取进程物理内存占用,单位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);
// 获取物理内存总量,单位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;
  // 查询进程内存工具集
  if (!QueryWorkingSet(hProcess, &workset_info, sizeof(workset_info))) {
    if (GetLastError() == ERROR_BAD_LENGTH) {
      // 计算所需内存,由于工具集长度动态变化,因此在此多申请64个工具集空间。
      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++) {
          // 判断工具集是否共享
          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单位转换KB单位
  *mem_used_size = (*mem_used_size / 1024);
  return true;
}

7. 用户名

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;
  // 打开进程令牌
  if (OpenProcessToken(handle, TOKEN_QUERY, &token)) {
    DWORD token_size = 0;
    PTOKEN_USER p_token_user = NULL;
    SID_NAME_USE sn;
    // 从进程令牌中获取用户令牌
    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;
            // 根据用户令牌查询用户名
            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. 网络连接

1)linux

/proc/net/tcp /proc/net/tcp6 /proc/net/udp /proc/net/udp6 : 提供了当前 TCP /TCP6/UDP/UDP6套接字的详细信息。
/proc/[PID]/fd/:这是一个文件夹,包含了进程当前打开的文件描述符列表。

  • 首先,解析套接字信息,包括套接字的inode号;
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];
	// 解析TCP信息
    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地址
    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]);
      // 端口序转换
      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地址
      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];
	// 解析UDP信息
    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地址
      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地址
      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);
}
  • 遍历所有进程的fd文件夹,匹配inode号。

2)WIndows

直接使用GetExtendedTcpTable和GetExtendedUdpTable函数查询TCP和UDP信息。

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. 待续

……

你可能感兴趣的:(杂记,c++)