需強制關閉佔用檔案的進程時,單純 TerminateProcess 易造成資料遺失、資源洩漏。本文實作 CFindKillProcess 類,遵循「禮貌請求 → 等待退出 → 強制終止」三階段策略,並正確處理 WM_QUERYENDSESSION、SendMessageTimeout (含 SMTO_NOTIMEOUTIFNOTHUNG)、主視窗枚舉等細節,程式碼可直接重用。
WM_QUERYENDSESSION:詢問視窗是否允許結束會話,模擬登出/關機流程,給 App 機會彈出「儲存變更?」對話框。WM_CLOSE:標準關閉請求,App 可在 OnClose 執行清理。WaitForSingleObject 等待進程結束:最多等待 5 秒(可調)。TerminateProcess:僅當上述失敗且呼叫者允許強制終止時執行。| API | 關鍵參數/旗標 | 說明 |
|---|---|---|
SendMessageTimeout | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG | 若視窗未掛起則無限等待使用者回應對話框;掛起則立即返回。僅 Win2000/XP+ 支援。 |
PostMessage(WM_CLOSE) | 非同步發送 | 避免死結,配合等待機制。 |
OpenProcess | SYNCHRONIZE | PROCESS_TERMINATE | 取得同步與終止權限。 |
WaitForSingleObject | 逾時 5000ms | 等待進程物件發出訊號。 |
CProcessIteratorclass CProcessIterator {
DWORD* m_pids; DWORD m_count, m_current;
public:
CProcessIterator(); ~CProcessIterator();
DWORD First(); // 呼叫 EnumProcesses 初始化陣列,略過 PID=0 (Idle)
DWORD Next() { return m_pids && m_current < m_count ? m_pids[m_current++] : 0; }
DWORD GetCount() { return m_count; }
};
CFindKillProcessclass CFindKillProcess {
public:
DWORD FindProcess(LPCTSTR modname, BOOL bAddExe=TRUE); // 比對模組名 (含/不含 .exe)
BOOL KillProcess(DWORD pid, BOOL bZap); // bZap=TRUE 允許 TerminateProcess
};
DWORD CFindKillProcess::FindProcess(LPCTSTR modname, BOOL bAddExe)
{
CProcessIterator itp;
for (DWORD pid = itp.First(); pid; pid = itp.Next())
{
CProcessModuleIterator itm(pid);
HMODULE hMod = itm.First(); // .EXE 模組
if (hMod)
{
TCHAR name[_MAX_PATH];
GetModuleBaseName(itm.GetProcessHandle(), hMod, name, _MAX_PATH);
string sMod = modname;
if (strcmpi(sMod.c_str(), name) == 0) return pid;
if (bAddExe && strcmpi((sMod + ".exe").c_str(), name) == 0) return pid;
}
}
return 0;
}
BOOL CFindKillProcess::KillProcess(DWORD pid, BOOL bZap)
{
CMainWindowIterator itw(pid); // 列舉進程所有主視窗
for (HWND hwnd = itw.First(); hwnd; hwnd = itw.Next())
{
DWORD bOKToKill = FALSE;
// 詢問視窗是否允許結束會話
SendMessageTimeout(hwnd, WM_QUERYENDSESSION, 0, 0,
SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 100, &bOKToKill);
if (!bOKToKill) return FALSE; // 視窗拒絕 (使用者按 Cancel)
PostMessage(hwnd, WM_CLOSE, 0, 0); // 發送關閉請求
}
// 等待進程自然結束
BOOL bKilled = TRUE;
HANDLE hp = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid);
if (hp)
{
if (WaitForSingleObject(hp, 5000) != WAIT_OBJECT_0)
{
if (bZap) TerminateProcess(hp, 0); // 強制殺死
else bKilled = FALSE;
}
CloseHandle(hp);
}
return bKilled;
}
int main(int argc, TCHAR* argv[])
{
// 解析參數:/n (僅顯示), /q (靜默), /z (強制殺死)
// 遍歷指定模組名,呼叫 FindProcess + KillProcess
// 完整參數解析碼見原文下載原始碼
}
使用方式:kp.exe /z notepad 強制關閉所有記事本進程。
CProcessIterator — 系統進程列舉CProcessModuleIterator — 進程模組列舉 (取 .exe 基址/名稱)CMainWindowIterator — 進程主視窗列舉 (可見、擁有者、非子視窗)CFindKillProcess — 整合上述,提供高層介面SMTO_NOTIMEOUTIFNOTHUNG),Win9x 需調整旗標。Psapi.lib (EnumProcesses/GetModuleBaseName) 或動態載入。「乾淨終止」不只是呼叫 TerminateProcess,而是尊重應用程式的關閉協議。CFindKillProcess 封裝了工業級終止邏輯,可直接整合至安裝程式、監控工具、自動化測試框架中。
本文配套原始碼含完整類別定義與命令列工具主程式。