在 Windows 操作系统中,了解正在运行的进程的信息对于系统管理和性能优化至关重要。通过遍历系统进程信息,我们可以获取进程的 ID、名称、线程数、句柄数以及各种性能指标,从而帮助我们了解系统的运行状况并进行必要的调整和优化。本文将详细介绍一种通过调用 Windows 系统API来遍历系统进程信息的技术,同时提供一段用 C++ 编写的示例代码。
操作原理
Windows 操作系统提供了一系列API函数,可以用于获取系统状态和信息。其中之一是 NtQuerySystemInformation 函数,该函数可以用来获取各种系统信息,包括进程信息。通过调用 NtQuerySystemInformation 函数,并将系统信息结构体填充到提供的缓冲区中,我们可以获得系统中运行的所有进程的信息。
在示例代码中,首先定义了一系列结构体,用于存储进程和线程的详细信息。然后通过调用 NtQuerySystemInformation 函数来获取系统进程信息,遍历返回的进程信息结构体,逐个获取每个进程的信息,包括进程名、进程ID、句柄数、线程数等,并在控制台输出。
具体实现
示例代码中使用了以下关键技术:
- 使用 NtQuerySystemInformation 函数获取系统进程信息。
- 遍历返回的进程信息结构体,获取每个进程的详细信息。
- 输出获取到的进程信息到控制台。
示例代码解析
以下是示例代码中用到的关键部分:
- 定义了用于存储进程信息的 _SYSTEM_PROCESS_INFORMATION 结构体和用于存储线程信息的 _SYSTEM_THREAD_INFORMATION 结构体。
- 使用 NtQuerySystemInformation 函数获取系统进程信息,并遍历返回的进程信息结构体,逐个获取每个进程的信息并输出到控制台。
完整代码
#include <iostream>
#include <Windows.h>
#ifdef _WIN64
typedef ULONG64 KPRIORITY;
#else
typedef LONG KPRIORITY;
#endif
#define SystemProcessInformation 5 // 功能号
#ifdef _M_IX86
typedef struct _CLIENT_ID
{
DWORD UniqueProcess;
DWORD UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
#endif // x86模式下
#ifdef _M_X64
typedef struct _CLIENT_ID
{
ULONG64 UniqueProcess;
ULONG64 UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
#endif // x64模式下
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef enum _KWAIT_REASON
{
Executive = 0,
FreePage = 1,
PageIn = 2,
PoolAllocation = 3,
DelayExecution = 4,
Suspended = 5,
UserRequest = 6,
WrExecutive = 7,
WrFreePage = 8,
WrPageIn = 9,
WrPoolAllocation = 10,
WrDelayExecution = 11,
WrSuspended = 12,
WrUserRequest = 13,
WrEventPair = 14,
WrQueue = 15,
WrLpcReceive = 16,
WrLpcReply = 17,
WrVirtualMemory = 18,
WrPageOut = 19,
WrRendezvous = 20,
Spare2 = 21,
Spare3 = 22,
Spare4 = 23,
Spare5 = 24,
WrCalloutStack = 25,
WrKernel = 26,
WrResource = 27,
WrPushLock = 28,
WrMutex = 29,
WrQuantumEnd = 30,
WrDispatchInt = 31,
WrPreempted = 32,
WrYieldExecution = 33,
WrFastMutex = 34,
WrGuardedMutex = 35,
WrRundown = 36,
MaximumWaitReason = 37
} KWAIT_REASON;
// 线程结构体
typedef struct _SYSTEM_THREAD_INFORMATION
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
LONG BasePriority;
ULONG ContextSwitches;
ULONG ThreadState;
KWAIT_REASON WaitReason;
} SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;
// 进程结构体
typedef struct _SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER WorkingSetPrivateSize; // since VISTA
ULONG HardFaultCount; // since WIN7
ULONG NumberOfThreadsHighWatermark; // since WIN7
ULONGLONG CycleTime; // since WIN7
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
HANDLE InheritedFromUniqueProcessId;
ULONG HandleCount;
ULONG SessionId;
ULONG_PTR UniqueProcessKey; // since VISTA (requires SystemExtendedProcessInformation)
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER ReadOperationCount;
LARGE_INTEGER WriteOperationCount;
LARGE_INTEGER OtherOperationCount;
LARGE_INTEGER ReadTransferCount;
LARGE_INTEGER WriteTransferCount;
LARGE_INTEGER OtherTransferCount;
//SYSTEM_THREAD_INFORMATION Threads[1];
} SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION;
// 定义函数声明
typedef NTSTATUS (WINAPI* __QuerySystemInformation) (
UINT systemInformation,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
BOOL QueryProcessInformationCallAll() {
ULONG cbSize = sizeof(SYSTEM_PROCESS_INFORMATION);
__QuerySystemInformation NtQuerySystemInformation = NULL;
HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
if (hNtDll == NULL)
{
wprintf(L"LoadLibrary Failed.\n");
return FALSE;
}
NtQuerySystemInformation = (__QuerySystemInformation)GetProcAddress(
hNtDll, "NtQuerySystemInformation");
PSYSTEM_PROCESS_INFORMATION sysPSIEntry = NULL, sysForwardLinks = NULL;
if (NtQuerySystemInformation == NULL)
{
wprintf(L"GetProcAddress Failed.\n");
return FALSE;
}
// 调用 NtQuerySystemInformation 查询进程信息
NTSTATUS status = 0;
status = NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &cbSize);
if (status == 0xC0000004)
{
sysPSIEntry = (PSYSTEM_PROCESS_INFORMATION)malloc(cbSize);
status = NtQuerySystemInformation(SystemProcessInformation, sysPSIEntry, cbSize, &cbSize);
if (status)
free(sysPSIEntry);
}
// 拷贝结构体指针副本
sysForwardLinks = sysPSIEntry;
// 使用指针副本遍历进程信息
while (sysForwardLinks->NextEntryOffset != 0)
{
if (sysForwardLinks->ImageName.Buffer != NULL)
{
wprintf(L"进程名:\t%s \t进程ID:%I64u \t句柄总数:%u \t线程总数:%u \n",
sysForwardLinks->ImageName.Buffer, (UINT64)sysForwardLinks->UniqueProcessId,
sysForwardLinks->HandleCount, sysForwardLinks->NumberOfThreads);
// 打印线程信息
PSYSTEM_THREAD_INFORMATION threadInfo = NULL;
threadInfo = (PSYSTEM_THREAD_INFORMATION)((UINT64)sysForwardLinks + sizeof(SYSTEM_PROCESS_INFORMATION));
DWORD curThreadIndex = 1;
do {
#ifdef _WIN64
wprintf(L"\t线程ID:%I64u \t起始地址:0x%I64X \t线程的状态码:%u\n",
threadInfo->ClientId.UniqueThread,
(UINT64)threadInfo->StartAddress,
threadInfo->ThreadState);
#else
wprintf(L"\t线程ID:%d \t起始地址:0x%I32X \t线程的状态码:%u\n",
threadInfo->ClientId.UniqueThread,
(UINT)threadInfo->StartAddress,
threadInfo->ThreadState);
#endif
++threadInfo;
} while (curThreadIndex++ < sysForwardLinks->NumberOfThreads);
wprintf(L"\n");
}
// 指针的加减运算的单位是根据所指向数据类型大小的。
// 字节指针就是1,所以加减运算没问题。
// 这里是结构体指针,所以必须转成数字类型再运算。
sysForwardLinks = (PSYSTEM_PROCESS_INFORMATION)((UINT64)sysForwardLinks + sysForwardLinks->NextEntryOffset);
}
// 释放内存
free(sysPSIEntry);
sysPSIEntry = NULL;
sysForwardLinks = NULL;
return TRUE;
}
int main()
{
setlocale(LC_ALL, ".utf8");// 转换控制台代码页编码为 UTF-8
QueryProcessInformationCallAll();
return 0;
}
测试结果截图:
总结
通过遍历系统进程信息,我们可以了解系统中运行的各种进程的详细信息,从而更好地进行系统管理和性能优化。本文介绍了一种通过调用 Windows 系统API来获取并遍历系统进程信息的技术,并提供了一段示例代码以帮助读者理解该技术的实现方法。希望本文对于对 Windows 系统进程信息感兴趣的读者有所帮助。
发布于:2024.02.07,更新于:2024.02.07