由于程序需要一直运行,所以做了一个检测程序是否正常的检测程序,发现程序占用CPU过高或者为0时重启主程序。
通常的获取CPU占有率的方法是NtQuerySystemInformation, NtQueryInformationProcess,但是由于它们不是public的,使用不方便,一个可观的替代方案是
GetSystemTimes和GetProcessTimes。
BOOL WINAPI GetSystemTimes(
_Out_opt_ LPFILETIME lpIdleTime,
_Out_opt_ LPFILETIME lpKernelTime,
_Out_opt_ LPFILETIME lpUserTime );
BOOL WINAPI GetProcessTimes(
_In_ HANDLE hProcess,
_Out_ LPFILETIME lpCreationTime,
_Out_ LPFILETIME lpExitTime,
_Out_ LPFILETIME lpKernelTime,
_Out_ LPFILETIME lpUserTime );
GetSystemTimes获得系统(自开机以来)处于Kernel状态下面的CPU时间,以及系统处于User状态下的时间,以及Idle的时间.我们只用Kernel时间和User时间, 不用Idle时间.
相应的, GetProcess也能求出一个进程在上面3中状态下的时间.
下面公式可以求出进程的CPU占用率.
代码如下:
/CpuUsage.h
#pragma once
class CpuUsage
{
public:
CpuUsage(DWORD dwProcessID);
~CpuUsage(void);
public:
ULONGLONG GetUsageEx();
ULONGLONG GetSystemNonIdleTimes();
ULONGLONG GetProcessNonIdleTimes();
private:
ULONGLONG SubtractTimes(const FILETIME &ftA,const FILETIME& ftB);
ULONGLONG AddTimes(const FILETIME& ftA,const FILETIME& ftB);
bool EnoughTimePassed();
inline bool IsFirstRun() const {return (m_dwLastRun==0);}
/system total times
FILETIME m_ftPrevSysKernel;
FILETIME m_ftPrevSysUser;
process times
FILETIME m_ftPrevProcKernel;
FILETIME m_ftPrevProcUser;
ULONGLONG m_ullPrevSysNonIdleTime;这个变量和后面的变量记录上次获得的非
idle的系统cpu时间和进程时间
ULONGLONG m_ullPrevProcNonIdleTime;/这个类只绑定一个进程,在构造函数里面初始化
ULONGLONG m_nCpuUsage;
ULONGLONG m_dwLastRun;
DWORD m_dwProcessID;
HANDLE m_hProcess;
volatile LONG m_lRunCount;
};
//CpuUsage.cpp
#include "StdAfx.h"
#include "CpuUsage.h"
#include <strsafe.h>
void ErrorMsg(LPTSTR lpszFunction);
BOOL SetPrivilege(HANDLE hProcess,LPCTSTR lpszPrivilege,BOOL bEnablePrivilege);
#ifdef USE_DEPRECATED_FUNCS
#define SystemBasicInformation 0
#define SystemPerformanceInformation 2
#define SystemTimeInformation 3
#define SystemProcessorPerformanceInformation 8
#define ProcessTimes 4
#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart))
typedef struct
{
DWORD dwUnknown1;
ULONG uKeMaximumIncrement;
ULONG uPageSize;
ULONG uMmNumberOfPhysicalPages;
ULONG uMmLowestPhysicalPage;
ULONG uMmHighestPhysicalPage;
ULONG uAllocationGranularity;
PVOID pLowestUserAddress;
PVOID pMmHighestUserAddress;
ULONG uKeActiveProcessors;
BYTE bKeNumberProcessors;
BYTE bUnknown2;
WORD wUnknown3;
} SYSTEM_BASIC_INFORMATION;
typedef struct
{
LARGE_INTEGER liIdleTime;
DWORD dwSpare[312];
} SYSTEM_PERFORMANCE_INFORMATION;
typedef struct
{
LARGE_INTEGER liKeBootTime;
LARGE_INTEGER liKeSystemTime;
LARGE_INTEGER liExpTimeZoneBias;
ULONG uCurrentTimeZoneId;
DWORD dwReserved;
} SYSTEMTEXTIME_INFORMATION;
typedef struct
_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
{
LARGE_INTEGER IdleTime;
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER Reserved1[2];
ULONG Reserved2;
} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
typedef struct _KERNEL_USERTEXTIMES
{
LARGE_INTEGER CreateTime;
LARGE_INTEGER ExitTime;
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
} KERNEL_USERTEXTIMES, *PKERNEL_USERTEXTIMES;
typedef LONG (WINAPI *PROCNTQSI)(UINT, PVOID, ULONG, PULONG);
PROCNTQSI NtQuerySystemInformation;
typedef LONG (WINAPI *PROCNTQIP)(HANDLE, UINT, PVOID, ULONG, PULONG);
PROCNTQIP NtQueryInformationProcess;
ULONGLONG CpuUsage::GetSystemNonIdleTimes()
{
SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
SYSTEMTEXTIME_INFORMATION SysTimeInfo;
SYSTEM_BASIC_INFORMATION SysBaseInfo;
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SysProcPerfInfo[32];
LONG status;
NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(GetModuleHandle(TEXT("ntdll")), "NtQuerySystemInformation");
if (!NtQuerySystemInformation)
return 0;
status = NtQuerySystemInformation(SystemBasicInformation, &SysBaseInfo, sizeof(SysBaseInfo), NULL);
if (status != NO_ERROR)
{
MessageBox(TEXT("FailSystemInfo"));
return 0;
}
status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, SysProcPerfInfo, sizeof(SysProcPerfInfo), NULL);
if(status != NO_ERROR) return 0;
int nProcessors = SysBaseInfo.bKeNumberProcessors; //机器内部CPU的个数
ULONGLONG ullSysTotal = 0;
for(int i = 0; i < nProcessors; i++)
{
ullSysTotal += SysProcPerfInfo[i].KernelTime.QuadPart + SysProcPerfInfo[i].UserTime.QuadPart;
}
return ullSysTotal;
}
ULONGLONG CpuUsage::GetProcessNonIdleTimes()
{
KERNEL_USERTEXTIMES KernelUserTimes;
::ZeroMemory(&KernelUserTimes, sizeof(KernelUserTimes));
NtQueryInformationProcess = (PROCNTQIP)GetProcAddress(GetModuleHandle(TEXT("ntdll")), "NtQueryInformationProcess");
LONG status = NtQueryInformationProcess(m_hProcess, ProcessTimes, &KernelUserTimes, sizeof(KernelUserTimes), NULL);
if(status == 0)
{
ErrorExit(TEXT("GetProcessNonIdleTimes"));
return 0;
}
return KernelUserTimes.KernelTime.QuadPart + KernelUserTimes.UserTime.QuadPart;
}
#endif
CpuUsage::CpuUsage(DWORD dwProcessID):m_nCpuUsage(0),
m_dwLastRun(0),
m_lRunCount(0),
m_dwProcessID(dwProcessID),
m_ullPrevProcNonIdleTime(0),
m_ullPrevSysNonIdleTime(0)
{
HANDLE hProcess = GetCurrentProcess();
SetPrivilege(hProcess,SE_DEBUG_NAME,TRUE);
m_hProcess=::OpenProcess(PROCESS_QUERY_INFORMATION,TRUE,m_dwProcessID);
if (m_hProcess==NULL)
{
ErrorMsg(TEXT("OpenProcess Fail"));
}
ZeroMemory(&m_ftPrevSysKernel, sizeof(FILETIME));
ZeroMemory(&m_ftPrevSysUser, sizeof(FILETIME));
ZeroMemory(&m_ftPrevProcKernel, sizeof(FILETIME));
ZeroMemory(&m_ftPrevProcUser, sizeof(FILETIME));
}
CpuUsage::~CpuUsage(void)
{
}
ULONGLONG CpuUsage::SubtractTimes(const FILETIME &ftA, const FILETIME &ftB)
{
LARGE_INTEGER a, b;
a.LowPart = ftA.dwLowDateTime;
a.HighPart = ftA.dwHighDateTime;
b.LowPart = ftB.dwLowDateTime;
b.HighPart = ftB.dwHighDateTime;
return a.QuadPart - b.QuadPart;
}
ULONGLONG CpuUsage::AddTimes(const FILETIME &ftA, const FILETIME &ftB)
{
LARGE_INTEGER a, b;
a.LowPart = ftA.dwLowDateTime;
a.HighPart = ftA.dwHighDateTime;
b.LowPart = ftB.dwLowDateTime;
b.HighPart = ftB.dwHighDateTime;
return a.QuadPart + b.QuadPart;
}
bool CpuUsage::EnoughTimePassed()
{
const int minElapsedMS = 250;//milliseconds
ULONGLONG dwCurrentTickCount = GetTickCount();
return (dwCurrentTickCount - m_dwLastRun) > minElapsedMS;
}
#ifndef USE_DEPRECATED_FUNCS
ULONGLONG CpuUsage::GetSystemNonIdleTimes()
{
FILETIME ftSysIdle, ftSysKernel, ftSysUser;
if(!GetSystemTimes(&ftSysIdle, &ftSysKernel, &ftSysUser))
{
ErrorMsg(TEXT("GetSystemTimes"));
return 0;
}
return AddTimes(ftSysKernel, ftSysUser);
}
ULONGLONG CpuUsage::GetProcessNonIdleTimes()
{
FILETIME ftProcCreation, ftProcExit, ftProcKernel, ftProcUser;
if(!GetProcessTimes(m_hProcess, &ftProcCreation, &ftProcExit, &ftProcKernel, &ftProcUser) && false)
{
ErrorMsg(TEXT("GetProcessNonIdleTimes"));
return 0;
}
return AddTimes(ftProcKernel, ftProcUser);
}
#endif
ULONGLONG CpuUsage::GetUsageEx()
{
ULONGLONG nCpuCopy = m_nCpuUsage;
if (::InterlockedIncrement(&m_lRunCount) == 1)
{
if (!EnoughTimePassed())
{
::InterlockedDecrement(&m_lRunCount);
return nCpuCopy;
}
ULONGLONG ullSysNonIdleTime = GetSystemNonIdleTimes();
ULONGLONG ullProcNonIdleTime = GetProcessNonIdleTimes();
if (!IsFirstRun())
{
ULONGLONG ullTotalSys = ullSysNonIdleTime - m_ullPrevSysNonIdleTime;
if(ullTotalSys == 0)
{
::InterlockedDecrement(&m_lRunCount);
return nCpuCopy;
}
m_nCpuUsage = ULONGLONG((ullProcNonIdleTime - m_ullPrevProcNonIdleTime) * 100.0 / (ullTotalSys));
m_ullPrevSysNonIdleTime = ullSysNonIdleTime;
m_ullPrevProcNonIdleTime = ullProcNonIdleTime;
}
m_dwLastRun = (ULONGLONG)GetTickCount();
nCpuCopy = m_nCpuUsage;
}
::InterlockedDecrement(&m_lRunCount);
return nCpuCopy;
}
BOOL SetPrivilege(HANDLE hProcess, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
HANDLE hToken;
if(!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
{
ErrorMsg(TEXT("OpenProcessToken"));
return FALSE;
}
LUID luid;
if(!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
{
ErrorMsg(TEXT("LookupPrivilegeValue"));
return FALSE;
}
TOKEN_PRIVILEGES tkp;
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = luid;
tkp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : FALSE;
if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
{
ErrorMsg(TEXT("AdjustTokenPrivileges"));
return FALSE;
}
return TRUE;
}
void ErrorMsg(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
LANG_USER_DEFAULT,
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(dw);
}
/测试函数
DWORD dwProcId =9840;
CpuUsage cu(dwProcId);
SYSTEMTIME st;
while(true)
{
GetLocalTime(&st);
char Msg[MAX_PATH]={0};
sprintf(Msg,"Process(pid:%d) uses %I64d%% cpu at %02d:%02d.%02d\n", dwProcId, cu.GetUsageEx(), st.wHour, st.wMinute, st.wSecond);
MessageBoxA(NULL,Msg,"msg",0);
::Sleep(second);
}