当前位置: 首页 > news >正文

Windows 10系统编程——进程专题:枚举我们进程的状态

Windows 10系统编程——进程专题:枚举我们进程的状态

前言

​ 接下来的部分更多涉及到一些代码实战的部分。比如说我们很快会尝试枚举一下咱们进程的状态。

Psapi.h

​ EnumProcess不在Windows.h中,他在另一个扩展的模块Psapi.h中,Psapi的全程是Process Status API,也就是获取咱们的进程的状态的API。需要注意的是(太操蛋了Windows SDK),Psapi.h需要在Windows.h之后被包含,要不然会出现找不到符号的问题。

​ PsAPI下的函数能做的事情非常的多

Psapi.hWindows 平台的进程状态和性能信息 API 头文件,它是 Process Status API 的一部分。

  • 提供了 查询系统进程、模块、内存信息等功能 的接口。
  • 通常配合 Psapi.lib 链接使用。
  • 可以用来实现类似任务管理器的功能:获取进程列表、模块列表、内存占用信息等。

我们稍后会介绍更加现代的ToolHelp32来取代比较反人类的EnumProcess,但是这里算是编程练习了,先放在这里。

函数用途
EnumProcesses获取系统中所有进程的 PID 列表
EnumProcessModules获取指定进程加载的模块句柄列表
GetModuleBaseName获取模块(通常是 EXE/DLL)的名称
GetModuleFileNameEx获取模块的完整路径
GetModuleInformation获取模块的基本信息(基址、大小等)
GetProcessMemoryInfo获取进程内存占用信息
EmptyWorkingSet清空指定进程的工作集,释放内存(谨慎使用)
QueryWorkingSet获取进程的工作集信息
EnumDeviceDrivers获取系统加载的驱动程序列表
GetDeviceDriverFileName获取驱动程序文件路径

使用EnumProcess来获取进程的PID

​ 获取进程的所有PID,咱们就来使用EnumProcess来解决这个小问题。

​ EnumProcess是试探性的,也就是说——他不计划告诉你到底系统有多少个API,需要你自己去试探获取。下面的例子就是一个典型的试探类型代码。

#include <Windows.h>
#include <Psapi.h>
#include <iostream>
#include <memory>static constexpr const int fromPidMemoryPoolSize(const DWORD d) {return d / sizeof(DWORD);
}static constexpr const DWORD toPidMemoryPoolSize(const int s) {return s * sizeof(DWORD);
}int main()
{int maxCount = 256; // try to make a array of pidsauto pids_pool = std::make_unique<DWORD[]>(maxCount);int real_pid_size = 0; // real size of the PIDSwhile (true) {DWORD actual_buffer_size;if (!EnumProcesses(pids_pool.get(),toPidMemoryPoolSize(maxCount),&actual_buffer_size)) {// function invoke failed!std::cout << "function EnumProcesses invoke failed!" << std::endl;return -1;}real_pid_size = toPidMemoryPoolSize(actual_buffer_size);if (real_pid_size < maxCount) break; // OK, we reach the maxmaxCount *= 2; // Try Largerpids_pool = std::make_unique<DWORD[]>(maxCount);}printf("The Pid pool size: %d", real_pid_size);return 0;
}

使用OpenProcess来访问线程句柄

OpenProcess 是 Windows API(在 Windows.h 中声明)的一个函数,用于以指定访问权限打开另一个进程并返回其句柄。常见用途包括:查询进程信息、读取/写入进程内存(调试或监控工具)、等待进程结束或向其发送控制操作。注意:访问受限的进程(系统、受保护进程或高权限进程)需要更高权限或管理员权限,滥用可能触发安全/隐私问题。

​ 下面的内容是笔者从MSDN文档中摘录出来的——

#include <windows.h>HANDLE OpenProcess(DWORD dwDesiredAccess,BOOL  bInheritHandle,DWORD dwProcessId
);
  • dwDesiredAccess:所需权限(如 PROCESS_QUERY_INFORMATIONPROCESS_VM_READPROCESS_VM_WRITEPROCESS_TERMINATE、或 PROCESS_ALL_ACCESS 等)。
  • bInheritHandle:子进程是否可继承该句柄(通常为 FALSE)。
  • dwProcessId:目标进程的 PID(进程 ID)。

返回值与错误处理

  • 成功返回有效 HANDLE,失败返回 NULL。失败时可用 GetLastError() 获取错误码(例如 ERROR_ACCESS_DENIEDERROR_INVALID_PARAMETER 等)。
  • 使用完句柄后必须调用 CloseHandle(hProcess) 以释放资源。
#include <windows.h>
#include <iostream>int main() {DWORD pid = 1234; // 目标进程 PID(示例)HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);if (!h) {std::cerr << "OpenProcess failed, error: " << GetLastError() << '\n';return 1;}// 示例:检查进程是否仍然存在(等待 0 毫秒)DWORD wait = WaitForSingleObject(h, 0);if (wait == WAIT_TIMEOUT) {std::cout << "Process " << pid << " is running.\n";} else {std::cout << "Process " << pid << " is not running or terminated.\n";}CloseHandle(h);return 0;
}

​ 基于这个思路,咱们就可以开始枚举进程的基本信息了,如下所示:

#include <Windows.h>
#include <Psapi.h>
#include <iostream>
#include <memory>
#include <vector>
#include <map>
#include <tchar.h>
static constexpr const int fromPidMemoryPoolSize(const DWORD d) {return d / sizeof(DWORD);
}static constexpr const DWORD toPidMemoryPoolSize(const int s) {return s * sizeof(DWORD);
}int main()
{int maxCount = 256; // try to make a array of pidsauto pids_pool = std::make_unique<DWORD[]>(maxCount);int real_pid_size = 0; // real size of the PIDSwhile (true) {DWORD actual_buffer_size;if (!EnumProcesses(pids_pool.get(),toPidMemoryPoolSize(maxCount),&actual_buffer_size)) {// function invoke failed!std::cout << "function EnumProcesses invoke failed!" << std::endl;return -1;}real_pid_size = toPidMemoryPoolSize(actual_buffer_size);if (real_pid_size < maxCount) break; // OK, we reach the maxmaxCount *= 2; // Try Largerpids_pool = std::make_unique<DWORD[]>(maxCount);}for (int i = 0; i < real_pid_size; i++) {HANDLE current_process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pids_pool[i]);if (current_process_handle == NULL) // Not a valid handle{... // error donecontinue;}... // using the pid to collect relative infoCloseHandle(current_process_handle);}return 0;
}

利用GetProcessTimes获取进程相关的统计时间

​ GetProcessTimes可以用来获取进程相关的统计时间

BOOL GetProcessTimes([in]  HANDLE     hProcess,[out] LPFILETIME lpCreationTime,[out] LPFILETIME lpExitTime,[out] LPFILETIME lpKernelTime,[out] LPFILETIME lpUserTime
);

​ MSDN对下面的参数有着这些注解:

[in] hProcess

要获取其计时信息的进程的句柄。 句柄必须具有 PROCESS_QUERY_INFORMATIONPROCESS_QUERY_LIMITED_INFORMATION 访问权限。 有关详细信息,请参阅 进程安全和访问权限。

Windows Server 2003 和 Windows XP: 句柄必须具有 PROCESS_QUERY_INFORMATION 访问权限。

[out] lpCreationTime

指向 FILETIME 结构的指针,该结构接收进程的创建时间。

[out] lpExitTime

指向 FILETIME 结构的指针,该结构接收进程的退出时间。 如果进程尚未退出,则此结构的内容未定义。

[out] lpKernelTime

指向 FILETIME 结构的指针,该结构接收进程在内核模式下执行的时间。 确定进程的每个线程在内核模式下执行的时间,然后将所有这些时间相加以获取此值。

[out] lpUserTime

指向 FILETIME 结构的指针,该结构接收进程在用户模式下执行的时间。 确定进程的每个线程在用户模式下执行的时间,然后将所有这些时间相加以获取此值。

补充:FILETIME 与 SYSTEMTIME(Windows 时间相关结构)

  • FILETIME:Windows 内部常用的时间表示,表示自 1601-01-01(UTC) 起经过的 100 纳秒(0.1µs)间隔数。它是一个 64 位值(在 API 中用两个 32 位字段表示),适合做精确的时间算术、排序与存储。
  • SYSTEMTIME:可读的“拆分时间”结构(年、月、日、时、分、秒、毫秒),便于显示与格式化。它本身不携带时区信息;通常用来表示 UTC(通过 GetSystemTime )或本地时间(通过 GetLocalTime)。

结构定义(来自 Windows SDK)

// FILETIME
typedef struct _FILETIME {DWORD dwLowDateTime;DWORD dwHighDateTime;
} FILETIME;// SYSTEMTIME
typedef struct _SYSTEMTIME {WORD wYear;WORD wMonth;WORD wDayOfWeek; // Sunday = 0WORD wDay;WORD wHour;WORD wMinute;WORD wSecond;WORD wMilliseconds;
} SYSTEMTIME;

SYSTEMTIME 是分解后的字段,便于显示/输入,但不便于直接做差值计算(先转为 FILETIME)。

一些可以知道的常用 API(转换与获取)
  • GetSystemTimeAsFileTime(&ft):以 UTC 返回当前时间的 FILETIME。
  • GetSystemTime(&st) / GetLocalTime(&st):以拆分字段返回当前 UTC / 本地时间。
  • SystemTimeToFileTime(const SYSTEMTIME*, FILETIME*):SYSTEMTIME → FILETIME(结果为 UTC)。
  • FileTimeToSystemTime(const FILETIME*, SYSTEMTIME*):FILETIME → SYSTEMTIME(结果为 UTC)。
  • FileTimeToLocalFileTime(const FILETIME*, FILETIME*)LocalFileTimeToFileTime:在 FILETIME(UTC)与本地 FILETIME(含时区/夏令时)之间转换。

​ 基于上述事实,咱们很容易编写获取时间的代码:

		HANDLE current_process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pids_pool[i]);if (current_process_handle == NULL) // Not a valid handle{...continue;}FILETIME start_time {0}, dummy;GetProcessTimes(current_process_handle, &start_time, &dummy, &dummy, &dummy);FileTimeToLocalFileTime(&start_time, &start_time);SYSTEMTIME system_time;FileTimeToSystemTime(&start_time, &system_time);

利用QueryFullProcessImageName获取执行文件名称

QueryFullProcessImageName 是 Windows API(kernel32)提供的用于获取指定进程可执行映像完整路径的函数。它比早期的 GetModuleFileNameEx / GetProcessImageFileName 更现代,且在可用权限受限的场景下可与 PROCESS_QUERY_LIMITED_INFORMATION 配合使用来读取路径信息。

// Unicode 版本
BOOL QueryFullProcessImageNameW(HANDLE hProcess,DWORD  dwFlags,LPWSTR lpExeName,PDWORD lpdwSize
);
参数说明
  • hProcess:目标进程的句柄。通常通过 OpenProcess 获取(见下面示例)。
  • dwFlags:决定返回路径的格式:
    • 0:以 Win32 风格路径返回(例如 C:\Program Files\...)。
    • PROCESS_NAME_NATIVE(值 1):以原生 NT 风格路径返回(例如 \Device\HarddiskVolumeX\...)。
  • lpExeName:调用者提供的缓冲区,用于接收路径(对 Unicode 版本为 WCHAR[])。
  • lpdwSize:输入时表示缓冲区的容量(以字符数为单位);返回时写回实际写入的字符数(不包括终止 NUL)。调用者需准备足够大的缓冲区,或按需重试。
返回值与错误
  • 返回非零表示成功;失败返回 0,可调用 GetLastError() 获取错误码(常见错误:ERROR_INSUFFICIENT_BUFFERERROR_ACCESS_DENIEDERROR_PARTIAL_COPY 等)。在某些特殊目标(例如 WSL 或某些驱动映射的进程)上,使用不同 dwFlags 可能导致不同错误/不同行为。
所需访问权限 & 兼容性
  • 推荐用 OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, ...)(Windows Vista 及以后支持)。在旧版(如 Windows XP)上该常量可能不受支持,函数本身也在 Vista 及之后的平台更常见。若目标是高权限/受保护进程,可能仍需更高权限(如 SeDebugPrivilege)。

整理一下咱们的代码

#include <Windows.h>
#include <Psapi.h>
#include <iostream>
#include <memory>
#include <vector>
#include <map>
#include <tchar.h>
static constexpr const int fromPidMemoryPoolSize(const DWORD d) {return d / sizeof(DWORD);
}static constexpr const DWORD toPidMemoryPoolSize(const int s) {return s * sizeof(DWORD);
}int main()
{int maxCount = 256; // try to make a array of pidsauto pids_pool = std::make_unique<DWORD[]>(maxCount);int real_pid_size = 0; // real size of the PIDSwhile (true) {DWORD actual_buffer_size;if (!EnumProcesses(pids_pool.get(),toPidMemoryPoolSize(maxCount),&actual_buffer_size)) {// function invoke failed!std::cout << "function EnumProcesses invoke failed!" << std::endl;return -1;}real_pid_size = toPidMemoryPoolSize(actual_buffer_size);if (real_pid_size < maxCount) break; // OK, we reach the maxmaxCount *= 2; // Try Largerpids_pool = std::make_unique<DWORD[]>(maxCount);}printf("The Pid pool size: %d", real_pid_size);std::map<DWORD, std::vector<DWORD>> error_pids;struct DumpInfo {DumpInfo(const SYSTEMTIME& sys_start, const TCHAR exeName[MAX_PATH], const DWORD pid) {file_start_system = sys_start;// 复制 exeName 内容_tcsncpy_s(this->exeName, exeName, MAX_PATH);this->pid = pid;}SYSTEMTIME file_start_system;TCHAR exeName[MAX_PATH];DWORD pid;};std::vector<DumpInfo> dumps;for (int i = 0; i < real_pid_size; i++) {HANDLE current_process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pids_pool[i]);if (current_process_handle == NULL) // Not a valid handle{error_pids[GetLastError()].emplace_back(pids_pool[i]);continue;}FILETIME start_time {0}, dummy;GetProcessTimes(current_process_handle, &start_time, &dummy, &dummy, &dummy);FileTimeToLocalFileTime(&start_time, &start_time);SYSTEMTIME system_time;FileTimeToSystemTime(&start_time, &system_time);TCHAR exeName[MAX_PATH];DWORD size = MAX_PATH;DWORD count = ::QueryFullProcessImageName(current_process_handle, 0, exeName, &size);dumps.emplace_back(system_time, exeName, pids_pool[i]);CloseHandle(current_process_handle);}for (const auto& each : error_pids) {printf("The errno pids: %d occurs in the pids: ", each.first);int sz = each.second.size();for (int i = 0; i < sz; i++) {if (i % 5 == 0) printf("\n");printf("%d\t", each.second[i]);}}printf("\n");for (const auto& each : dumps) {printf("PID: %5d, Start: %d/%d/%d %02d:%02d:%02d Image: %ws\n",each.pid, each.file_start_system.wDay, each.file_start_system.wMonth,each.file_start_system.wYear, each.file_start_system.wHour, each.file_start_system.wMinute, each.file_start_system.wSecond,each.exeName);}return 0;
}

利用现代的CreateToolhelp32Snapshot来枚举进程

CreateToolhelp32Snapshot 是 Windows 提供的用于捕获系统当前进程/线程/模块等信息“快照”的函数。结合 Thread32First / Thread32Next 可以遍历系统中当前存在的线程列表,常用于进程监控、诊断工具或简单的任务管理器实现中。但是麻烦的一点在于CreateToolhelp32Snapshot是快照版本的,他只会保存调用的那一瞬间的进程快照的内容

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
  • TH32CS_SNAPPROCESS:枚举系统所有进程
  • TH32CS_SNAPTHREAD:枚举系统所有线程
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
int main()
{HANDLE hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (hSnapshot == INVALID_HANDLE_VALUE) {std::cerr << "failed to involk CreateToolhelp32Snapshot";return -1;}PROCESSENTRY32 pe;pe.dwSize = sizeof(pe);if (!Process32First(hSnapshot, &pe)) {std::cerr << "failed to involk Process32First";return -1;}do {printf("PID:%6d (PPID:%6d): %ws (Threads=%d) (Priority=%d)\n",pe.th32ProcessID, pe.th32ParentProcessID, pe.szExeFile,pe.cntThreads, pe.pcPriClassBase);} while (::Process32Next(hSnapshot, &pe));::CloseHandle(hSnapshot);return 0;
}

WTS 枚举

Windows 提供了一组 WTS(Windows Terminal Services)函数,用于查询、管理和控制会话(Session)及其相关进程。这类函数通常用于远程桌面管理、会话监控或系统管理工具中。

函数功能
WTSOpenServer打开一个远程或本地服务器的句柄(可用 WTS_CURRENT_SERVER_HANDLE 代替本地)
WTSEnumerateSessions枚举服务器上的所有会话
WTSQuerySessionInformation查询指定会话的详细信息(用户名、域、登录时间等)
WTSTerminateSession终止指定会话
WTSCloseServer关闭通过 WTSOpenServer 打开的句柄
WTSEnumerateProcesses枚举指定会话或服务器上的进程

所有 WTS API 都在 Wtsapi32.dll 中实现,头文件为 WtsApi32.h,使用时需链接 Wtsapi32.lib

PWTS_PROCESS_INFO pProcessInfo = nullptr;
DWORD processCount = 0;// 枚举服务器所有会话的进程
if (WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pProcessInfo, &processCount)) {for (DWORD i = 0; i < processCount; ++i) {WTS_PROCESS_INFO pi = pProcessInfo[i];std::wcout << L"PID: " << pi.ProcessId<< L"  SessionID: " << pi.SessionId<< L"  Image: " << pi.pProcessName << L"\n";}WTSFreeMemory(pProcessInfo);
}
http://www.dtcms.com/a/438843.html

相关文章:

  • 【开题答辩实录分享】以《基于协同过滤算法的经济型酒店推荐系统》为例进行答辩实录分享
  • 湘潭网站建设 都来磐石网络wordpress首页设计
  • labelme 创建AI多边形功能
  • 生物信息 R语言和 cytoscape 相互沟通的组件RCy3,构建cytoscape网络表 节点类型表 链接边的表,并推送到cytoscape
  • 你们需要网站建设怎么注销公司法人身份
  • 序列化和反序列化(redis为例)
  • 之江汇学校网站建设wordpress 不显示评论
  • 洛谷 - 背包题目详解(超详细版)
  • 自主进化的AI大模型架构设想(解决大模型时效性问题):知识网络的拓扑设计
  • 网站建设所需费用明细应不应该购买老域名建设新网站
  • 突破传统!基于SAM架构的双模态图像分割:让AI“看见“红外与可见光的完美融合
  • Agentic Schemas:构建未来多智能体协作架构的实践蓝图
  • 血玥珏-MIDI音符合理性筛选处理器 v1.0.0.6 使用说明
  • 网站建设维诺之星抖音seo排名源码
  • 深入理解:Rust 的内存模型
  • 深圳建站公司推荐宣传片制作费用报价表
  • Zig 语言通用代码生成器:逻辑冒烟测试版五,数据库自动反射功能
  • 基于 GEE 制作研究区遥感影像可用性地图
  • 微PE | 辅助安装Window系统
  • 企业网站怎么维护易语言做试用点击网站
  • (单调栈)洛谷 P6875 COCI 2013/2014 KRUŽNICE 题解
  • 地图网站怎么做中国的外贸企业有哪些
  • 外贸公司网站怎么设计更好单页响应式网站模板
  • 恒生电子面经准备
  • 电视剧在线观看完整版免费网站网友让你建网站做商城
  • 大学网站群建设方案设计网名姓氏
  • Qt 按钮点击事件全链路解析:从系统驱动到槽函数
  • 外贸公司建网站一般多少钱京津冀协同发展现状
  • 开发网站 语言优秀营销软文范例300字
  • 木匠手做网站网站主体变更