Windows 沒有提供單一 GetCpuUsage() API。取得 CPU 使用率需根據作業系統版本採用不同機制:
NtQuerySystemInformation 讀取系統效能計數器 (閒置時間、系統時間)。HKEY_DYN_DATA 讀取虛擬裝置驅動程式 (VxD) 維護的統計計數器。本文提供兩套完整 C 語言主控台程式碼,分別示範兩種平台的實作邏輯。
CPU 使用率 = 100% - (閒置時間增量 / 系統時間增量) * 100% / 處理器數
// 系統資訊類別代碼 (未文件化)
#define SystemBasicInformation 0 // 取得處理器數量等基本資訊
#define SystemPerformanceInformation 2 // 取得閒置時間 (LARGE_INTEGER)
#define SystemTimeInformation 3 // 取得系統啟動/當前時間
// 從 ntdll.dll 動態取得函式指標
typedef LONG (WINAPI *PFN_NtQuerySystemInformation)(
UINT SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
LoadLibrary("ntdll.dll") + GetProcAddress("NtQuerySystemInformation") 取得函式指標。SystemBasicInformation 取得 bKeNumberProcessors (邏輯處理器數)。SystemTimeInformation 取得 liKeSystemTime (系統時間)。SystemPerformanceInformation 取得 liIdleTime (閒置時間)。Sleep(1000)。#include
#include
#include
// LARGE_INTEGER 轉 double 輔助宏
#define Li2Double(li) ((double)(li).HighPart * 4.294967296E9 + (double)(li).LowPart)
typedef struct { // SystemBasicInformation 部分欄位
BYTE bKeNumberProcessors;
// ... 其餘略
} SYSTEM_BASIC_INFORMATION;
typedef struct { // SystemPerformanceInformation
LARGE_INTEGER liIdleTime;
DWORD dwSpare[76];
} SYSTEM_PERFORMANCE_INFORMATION;
typedef struct { // SystemTimeInformation
LARGE_INTEGER liKeSystemTime;
// ... 其餘略
} SYSTEM_TIME_INFORMATION;
int main() {
PFN_NtQuerySystemInformation NtQuerySystemInformation =
(PFN_NtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
if (!NtQuerySystemInformation) { puts("無法取得 NtQuerySystemInformation"); return 1; }
SYSTEM_BASIC_INFORMATION BasicInfo;
if (NtQuerySystemInformation(SystemBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL) != 0)
return 1;
SYSTEM_PERFORMANCE_INFORMATION PerfInfo;
SYSTEM_TIME_INFORMATION TimeInfo;
LARGE_INTEGER OldIdle = {0}, OldSys = {0};
double cpuUsage = 0.0;
printf("CPU Usage (press any key to exit): ");
while (!_kbhit()) {
if (NtQuerySystemInformation(SystemTimeInformation, &TimeInfo, sizeof(TimeInfo), NULL) != 0) break;
if (NtQuerySystemInformation(SystemPerformanceInformation, &PerfInfo, sizeof(PerfInfo), NULL) != 0) break;
if (OldIdle.QuadPart != 0) { // 非第一次取樣
double idleDelta = Li2Double(PerfInfo.liIdleTime) - Li2Double(OldIdle);
double sysDelta = Li2Double(TimeInfo.liKeSystemTime) - Li2Double(OldSys);
double idleRatio = idleDelta / sysDelta; // 0.0 ~ 1.0
cpuUsage = 100.0 - idleRatio * 100.0 / BasicInfo.bKeNumberProcessors;
if (cpuUsage < 0) cpuUsage = 0;
if (cpuUsage > 100) cpuUsage = 100;
printf("\b\b\b\b%3.0f%%", cpuUsage);
}
OldIdle = PerfInfo.liIdleTime;
OldSys = TimeInfo.liKeSystemTime;
Sleep(1000);
}
printf("\n");
return 0;
}
Win9x 核心虛擬裝置驅動程式 (VxD) 定期更新動態登錄檔樹 HKEY_DYN_DATA 下的效能計數器:
PerfStats\StartStat:啟動計數器 (需先讀取一次啟動)。PerfStats\StatData:目前計數器值,KERNEL\CPUUsage 即為 0~100 整數百分比。PerfStats\StopStat:停止計數器 (程式結束前讀取一次)。#include
#include
#include
int main() {
HKEY hKey;
DWORD dwType, dwSize, dwCpuUsage;
// 1. 啟動計數器
if (RegOpenKeyEx(HKEY_DYN_DATA, "PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
return 1;
dwSize = sizeof(DWORD);
RegQueryValueEx(hKey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwSize);
RegCloseKey(hKey);
// 2. 迴圈讀取目前值
if (RegOpenKeyEx(HKEY_DYN_DATA, "PerfStats\\StatData", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
return 1;
printf("CPU Usage (press any key to exit): ");
while (!_kbhit()) {
dwSize = sizeof(DWORD);
if (RegQueryValueEx(hKey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwSize) == ERROR_SUCCESS)
printf("\b\b\b\b%3d%%", dwCpuUsage);
Sleep(500); // 9x 更新頻率較高,可 500ms 取樣
}
printf("\n");
RegCloseKey(hKey);
// 3. 停止計數器 (清理)
if (RegOpenKeyEx(HKEY_DYN_DATA, "PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {
dwSize = sizeof(DWORD);
RegQueryValueEx(hKey, "KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwSize);
RegCloseKey(hKey);
}
return 0;
}
雖然 NtQuerySystemInformation 至今仍可用,但微軟建議改用正式文件化的 API:
PdhOpenQuery, PdhAddCounter (路徑 "\\Processor(_Total)\\% Processor Time"), PdhCollectQueryData, PdhGetFormattedCounterValue。支援本機/遠端、多語系、格式化輸出。SELECT LoadPercentage FROM Win32_Processor,適合腳本/管理工具,即時性較低。lpIdleTime, lpKernelTime, lpUserTime,邏輯與 NtQuerySystemInformation 相同但為正式 API。double GetCpuUsage() {
OSVERSIONINFOEX osvi = { sizeof(osvi) };
GetVersionEx((OSVERSIONINFO*)&osvi);
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
return GetCpuUsage_NT(); // 使用 GetSystemTimes 或 PDH
else
return GetCpuUsage_9x(); // 僅維舊系統相容
}
| 特性 | NT 方案 (NtQuerySystemInformation) | 9x 方案 (HKEY_DYN_DATA) | 現代建議 (PDH / GetSystemTimes) |
|---|---|---|---|
| 適用平台 | NT 4.0 ~ Windows 11 | 95 / 98 / Me | XP / 2003 ~ Windows 11 |
| API 性質 | 未文件化、內部函式 | 虛擬登錄檔、VxD 介面 | 正式文件化、穩定支援 |
| 精度 | 高 (100ns 解析度) | 整數百分比、更新較頻繁 | 高 |
| 多處理器支援 | 自動平均 | 單一數值 | 可分別查詢每顆處理器 |
| 部署相依性 | 無 (ntdll.dll 核心載入) | 無 | PDH 需連結 pdh.lib / 載入 pdh.dll |