目錄
一、項目概述
二、項目開發的各種功能關鍵
2.1 進程信息的獲取
2.2 線程信息的獲取
2.3 進程模塊信息的獲取
2.3.1 模塊快照
2.3.2 枚舉模塊
2.4 進程堆信息的獲取
2.5 窗口信息的獲取
2.6 文件信息的獲取
2.7 內存信息和CPU占用率的獲取
2.7.1 內存信息相關結構體
2.7.2 相關函數
2.7.3 使用例子
2.7.4 CPU占用率
三、其它功能要點
3.1 關機、重啟,注銷,休眠
3.2 老板鍵
?四、完整示例demo
4.1 完成的基礎功能
4.2 界面說明
4.3 操作說明
一、項目概述
????????在軟件開發領域,構建一個帶界面且功能豐富的項目往往充滿挑戰。本次項目不僅要應對多樣化的數據展示需求,涵蓋文件、進程、線程等多源信息,還需處理各類用戶交互操作。接下來,讓我們一同深入探索這個項目的關鍵要點,包括復雜數據的獲取方法、界面展示的考量,以及如關機操作、老板鍵設置等特色功能的實現途徑 ,為項目開發提供全面且清晰的指引,并提供實現demo。?
????????本次開發基礎除了MFC相關知識要求,還要熟悉windows sdk開發知識點,不會的請看前面的章節。下面簡單介紹該項目要點,界面不在此講述細節實現。
二、項目開發的各種功能關鍵
2.1 進程信息的獲取
????????在Windows系統里,要獲取當前正在運行的進程,可以依靠快照系列API 。這套API挺厲害的,它不光能把正在運行的進程找出來,還能獲取到進程的模塊列表、堆這些信息。使用這套API有這么幾個步驟:
(1)拍攝快照
(2)對快照進行遍歷
(3)把快照關閉
下面就是創建進程快照,然后獲取進程列表的具體做法:
//1.先創建一個進程快照
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 stcPe32;
//2.通過一組API遍歷進程快照
Process32First(hProcessSnap, &stcPe32);
do {//3.使用從進程快照中提取出來的信息,信息已經提取到stcPe32變量中//這些信息包含進程的PID, 進程名等數據,這些數據可以顯示到界面上//4.獲取快照中下一個進程的信息
} while (Process32Next(hProcessSnap, &stcPe32));
//5關閉快照
CloseHandle(hSanp);
2.2 線程信息的獲取
????????獲取線程信息的辦法和獲取進程信息差不多,也能用快照API來操作。但是,通過這個方法遍歷得到的線程,是系統里所有的線程,并不是屬于某一個特定進程的線程。這就可能出問題了,當我們要把這些線程數據顯示到界面上的時候,就容易產生沖突。比如說,我們已經遍歷出了進程列表,接下來想查看某個特定進程的線程列表,要是直接用剛才那種獲取所有線程的方式,得到的線程就不是我們想要的。不過還好,遍歷得到的線程信息里面,有一項是父進程的PID,利用這個PID進行篩選,就能找到指定進程的全部線程了。具體的操作方法如下:
//1.創建一個線程相關的快照句柄
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
//2.循環遍歷線程信息
//通過線程快照句柄獲取第一個線程信息
Thread32First(hThreadSnap, &stcTe32))
do {//判斷這個線程的父進程IDif (stcTe32.th320wnerProcessID == 指定進程的PID) {//將線程的信息顯示到界面.}//獲取快照中下一個線程信息
} while (Thread32Next(hThreadSnap, &stcTe32))
2.3 進程模塊信息的獲取
????????模塊信息也可以通過快照API來獲取。每個進程都有自己特有的模塊信息,當我們創建快照的時候,得指定一個進程的PID,這樣創建快照的函數就能根據這個PID,把指定進程的模塊列表給取出來。
????????在獲取模塊信息的過程中,經常會碰到一個問題,那就是一般只能獲取到32位進程的模塊信息,對于64位進程的模塊,就獲取不了了。不過有個解決辦法,就是用EnumProcessModulesEx函數,這個函數比較強大,32位和64位進程的模塊它都能獲取。但是要注意,使用這個函數有個前提,就是調用這個API的進程必須得是64位的。
????????下面就分別講講這兩種獲取模塊信息方法的具體使用:
2.3.1 模塊快照
//1.創建模塊快照
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPId);
//2.循環遍歷快照中的模塊信息
MODULEENTRY32 Module32;
Module32First(hModuleSnap, Module32);
do {//將獲取到的信息顯示到界面//4.獲取快照中下一個模塊信息
} while (Module32Next(hModuleSnap, &m_Module32));
CloseHandle(hSnap);
2.3.2 枚舉模塊
(想要同時遍歷出32位和64位進程的模塊,項目必須被編譯成64位的)
HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID/*進程ID*/);
HMODULE hModules[0x2000] = {}; // 只用于獲取模塊句柄個數
DWORD dwNeed = 0; // 獲取模塊句柄個數
//枚舉進程模塊,函數會輸出模塊句柄數組
EnumProcessModulesEx(hProcess, hModules, sizeof(hModules), &dwNeed, LIST_MODULES_ALL);
DWORD dwModuleCount = dwNeed / sizeof(HMODULE);
MODULEINFO moif = {0}; //保存模塊信息的結構體
//循環獲取模塊信息
for (SIZE_T i = 0; i < dwModuleCount; ++i) {//根據進程句柄和模塊句柄獲取模塊路徑GetModuleFileNameEx(hProcess, hModules[i], 緩沖區, 長度);//根據進程句柄和模塊句柄獲取模塊其他信息GetModuleInformation(hProcess, hModules[i], &moif, sizeof(MODULEINFO))moif.EntryPoint; //dl1入口點moif.lpBaseOfD11; //dll基址moif.SizeOfImage; //dll大小//將獲取到的模塊信息顯示到界面上.
}
2.4 進程堆信息的獲取
一個進程堆的數量眾多,但遍歷方式同樣可使用快照系列API。
//1.創建一個線程相關的快照句柄
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SMAPTHREAD, O);
//2.循環遍歷線程信息
do {//1)通過線程快照句柄獲取第一個線程信息Thread32First(hThreadSnap, &stcTe32))//2)將信息顯示到界面//3)獲取快照中下一個線程信息
} while (Thread32Next(hThreadSnap, &stcTe32))
2.5 窗口信息的獲取
????????窗口信息說的就是當前系統里,正在顯示的或者隱藏著的窗口的相關內容。在Windows系統里,EnumWindow函數專門用來把當前系統上所有窗口都列出來。這里的枚舉,意思就是Windows會逐個查看所有窗口,在這個查看的過程中,它會調用一個回調函數,然后把查看到的窗口信息傳送給這個回調函數。所以,要完成枚舉窗口這件事,實際上包含兩個部分:第一,調用EnumWinodw函數;第二,在回調函數里把枚舉出來的窗口信息保存好。?
????????下面就是枚舉窗口函數用到的API原型:?
BOOL CALLBACK EnumWindowsProc(_In_ HWND hwnd, //遍歷到的窗口句柄_In_ LPARAM 1Param //EnumWindows函數傳來的參數
);
枚舉窗口的用法為:
BOOL CALLBACK EnumWinProc(HWND hWnd, LPARAM 1Param) { //回調函數//根據窗口句柄獲取窗口名TCHAR buff[200];GetWindowText(hWnd, buff, 200); //得到窗口名,可顯示到界面中.//判斷窗口是否被隱藏if (IsWindowVisible(hWnd) == TRUE && wcslen(buff)!= 0)//窗口沒有被隱藏且窗口標題長度不為0,則將窗口信息顯示到界面中.
}
int main() {EnumWindow(&EnumWinProc/*枚舉窗口的回調函數*/, NULL/*回調函數的附加參數*/);
}
2.6 文件信息的獲取
????????Windows系統提供了一組能用來遍歷文件的API,分別是FindFirstFile和FindNextFile。FindFirstFile這個API的作用是找到一個文件夾里的第一個文件或者子文件夾。而FindNextFile呢,它負責一個接一個地去查找剩下的文件或文件夾,直到全部找完為止。所以,在使用這組API的時候,通常得先調用FindFirstFile,它會返回一個用來查找文件的句柄,拿到這個句柄后,再用FindNextFile去把剩下的文件都找出來。不過這組API有個局限性,它們只能找到當前目錄下的文件和文件夾。要是想把所有層級文件夾里的文件都找出來,那就得自己寫個遞歸函數才能做到了。
????????下面給你看看這兩個函數具體怎么簡單使用:
HANDLE hFind ;
WIN32_FIND_DATA fData;
hFind = FindFirstFile(L"D:\\*", &fData);
if (hFind == (HANDLE)-1) return ;
do {//將遍歷到的信息(fData結構體變量的內容)顯示到界面.
} while (FindNextFile(hFind, &fData));
2.7 內存信息和CPU占用率的獲取
2.7.1 內存信息相關結構體
typedef struct _MEMORYSTATUS {DWORD dwLength; //該結構體大小DWORD dwMemoryLoad; //當前系統內存的占用率(百分比)SIZE_T dwTotalPhys; //總的物理內存大小SIZE_T dwAvailPhys; //可能的物理內存大小以字節為單位SIZE_T dwTotalPageFile; //交換文件總大小SIZE_T dwAvailPageFile; //交換文件中空閑部分大小SIZE_T dwTotalVirtual; //總的虛擬內存大小SIZE_T dwAvailVirtual; //可用虛擬內存大小
}MEMORYSTATUS, *LPMEMORYSTATUS;
2.7.2 相關函數
獲取系統內存信息
void WINAPI GlobalMemoryStatus(_Out_ LPMEMORYSTATUS lpBuffer //MEMORYSTATUS結構體指針
);
2.7.3 使用例子
//1.創建結構體對象并調用獲取內存信息的函數
MEMORYSTATUS memStatus;
GlobalMemoryStatus(&memStatus);
//當前內存占用率:memStatus.dwMemoryLoad
//已用物理內存大小:memStatus.dwTotalPhys - memStatus.dwAvailPhys
2.7.4 CPU占用率
????????CPU占用率,說的就是在單位時間里,CPU處于被占用狀態的總時長。要算出這個數值,我們得先知道在一段時間內CPU實際被使用的時間,然后通過下面這個公式來計算它的占用率:
????????使用率 = 100.0 - (空閑時間÷使用時間)× 100.0;
????????至于獲取當前CPU被占用的時間,就得借助GetSystemTimes函數了。這個函數可以獲取到三個時間數據:CPU空閑下來的時間、CPU在內核態下被使用的時間,還有CPU在用戶態下被使用的時間。這里面,把CPU在內核態和用戶態被使用的時間加起來,得到的就是CPU總的使用時間。下面的代碼展示了具體計算CPU占用率的方法:
double FILETIME2Double(const _FILETIME& fileTime) {return double(fileTime.dwHighDateTime * 4.294967296e9) + double(fileTime.dwLowDateTime);
}
Int GetCpuUsage(){// 空閑時間 內核時間 用戶時間_FILETIME idleTime, kernelTime, userTime;//獲取時間GetSystemTimes(&idleTime, &kernelTime, &userTime);//等待1000毫秒HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);//等待1000毫秒,使用內核對象等待會更精確WaitForSingleObject(hEvent, 1000);//獲取新的時間_FILETIME newIdleTime, newKernelTime, newUserTime;GetSystemTimes(&newIdleTime, &newKernelTime, &newUserTime);//將各個時間轉換double d01dIdleTime = FILETIME2Double(idleTime);double dNewIdleTime = FILETIME2Double(newIdleTime);double d0IdKernelTime = FILETIME2Double(kernelTime);double dNewKernelTime = FILETIME2Double(newKernelTime);double d01dUserTime = FILETIME2Double(userTime);double dNewUserTime = FILETIME2Double(newUserTime);//計算出使用率return int(100.0 - (dNewIdleTime - d0ldIdleTime) / (dNewKernelTime - d01dKernelTime + dNewUserTime - d0ldUserTime) * 100.0);
}
三、其它功能要點
3.1 關機、重啟,注銷,休眠
????????關機、重啟、注銷以及休眠,這些操作都需要較高的權限才能執行。要是一個進程的權限不夠高,去執行這些操作就會失敗。所以,要是一個程序打算進行這些操作,第一步得用管理員權限來運行,第二步還得通過專門的權限提升函數,讓程序獲得關機的特權。具體的操作步驟是這樣的:?
????????(1)把關機特權獲取到?
????????(2)進行關機相關的操作?
????????下面這段代碼就是用來開啟關機特權的?
HANDLE hToken = NULL;
HANDLE hProcess = GetCurrentProcess(); //該函數能得到該進程的偽句柄
OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
TOKEN_PRIVILEGES tp = {0 };
LookupPrivilegeValue(0, SE_SHUTDOWN_NAME, &tp.Privileges[0].Luid));
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//調用函數提升權限
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
下面的代碼可用于關機等操作:
1. 關機:ExitWindowsEx(EWX_POWEROFF|EWX_FORCE);
2. 重啟:ExitWindowsEx(EWX_REBOOT|EWX_FORCE);
3. 注銷:ExitWindowsEx(EWX_LOGOFF|EWX_FORCE);
4. 休眠:SetSuspendState(TRUE,FALSE,FALSE);
5. 睡眠:SetSuspendState(FALSE,FALSE,FALSE);
6. 鎖屏:LockWorkStation();
3.2 老板鍵
????????老板鍵其實就是能快速響應全局按鍵,這樣不用激活窗口,按一組特定按鍵,窗口就能迅速“消失”。?
????????用這種加強版的快捷鍵分兩步。第一步,用RegisterHotKey函數向系統注冊一個全局熱鍵,注冊的時候能對熱鍵進行設置,像熱鍵的ID、快捷鍵之類的。函數原型如下:?
BOOL WINAPI RegisterHotKey(_In_opt_ HWND hWnd, //響應該熱鍵的窗口句柄_In_ int id, //該熱鍵的ID,ID值可以是自定義的一個數值_In_ UINT fsModifiers,//該熱鍵的輔助按鍵例如Ctrl鍵盤)_In_ UINT vk //該熱鍵的鍵值,一個按鍵碼
);
注冊熱鍵的例子如下:
//注冊一個快捷鍵:Ctrl +Shift+H
::RegisterHotKey(hWnd, //當前窗口的句柄0x1234, //自定義熱鍵ID值MOD_CONTROL|MOD_SHIFT, //同時按下ctrl,shift'h'); //ctrl+shift+h
????????第二步呢,要在消息響應函數里對WM_HOTKEY消息做出反應。這個消息的WPARAM參數,其實就是我們注冊熱鍵時自己設定的那個熱鍵ID 。接著在這個消息處理過程中,就能讓窗口顯示或者隱藏。?
????????要是用SDK開發,直接在回調函數里寫case WM_HOTKEY消息的處理代碼就行。但如果是用MFC開發,因為沒辦法直接編寫消息回調函數,就得在類向導里添加PreTranslateMessage虛函數來處理這個消息。下面就是相關代碼:?
BOOL CXXXDlg::PreTranslateMessage(MSG* pMsg) {//判斷消息是否是全局熱鍵消息,并判斷該熱鍵的ID是否是我們注冊過的if ((pMsg->message == WM_HOTKEY) && (pMsg->wParam == 0x1234)) {if (IsWindowVisible(m_hWnd) == TRUE)ShowWindow(SW_HIDE); //隱藏窗口elseShowWindow(SW_SHOW); //顯示窗口}return CDialogEx::PreTranslateMessage(pMsg);
}
?四、完整示例demo
MFC任務管理器說明書
4.1 完成的基礎功能
1 遍歷進程
2 遍歷線程
3 遍歷模塊
4 遍歷堆
5 結束進程,掛起線程,恢復運行線程,結束線程
6 獲取CPU,內存信息
7 遍歷文件信息:文件名,創建時間,修改時間,文件大小
8 遍歷窗口信息
9 掃描并清理VS工程垃圾
附加功能如下:
1 老板鍵及其他快捷鍵:如ctrl+7隱藏程序,再次恢復顯示程序
2 進程列表按列排序,根據整數或者字符串排序顯示
3 利用定時器自主設置進程表刷新速度
4 主菜單項:關機,重啟,注銷,休眠,睡眠,鎖屏
5 刷新窗口信息和關閉窗口
6 增加線程更新cpu和內存信息
7 查看有些堆信息有點卡頓,增加線程查看堆信息
開發環境:Windows10+VS2015+CPP
4.2 界面說明
1 菜單選項有:
刷新速度
關機 ctrl+1
重啟 ctrl+2
注銷 ctrl+3
休眠 ctrl+4
睡眠 ctrl+5
鎖屏 ctrl+6
隱藏 ctrl+7
2 CPU利用率 內存占用率
3 TAB選項
進程 文件 窗口 清理
進程:顯示進程列表,根據進程ID可以查看對應的線程,模塊和堆
文件:查看某文件夾或磁盤的文件
窗口:查看當前所有顯示窗口
清理:清理VS工程的工具
4.3 操作說明
????????可以鼠標左鍵單擊菜單選項操作,在進程列表,線程模塊堆和窗口列表可以使用鼠標右鍵菜單操作。其他子窗口看界面說明。
????????注意事項:程序操作中,如結束進程,掛起線程,恢復運行線程,結束線程,查看模塊,堆信息有些會失敗,可能因為權限不夠或者被限制。
進程
文件
窗口
清理
demo代碼:https://download.csdn.net/download/linshantang/90530350