首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 乾淨終止進程:從 WM_CLOSE 到 TerminateProcess 完整封裝類

     編輯:關於VC++

前言

需強制關閉佔用檔案的進程時,單純 TerminateProcess 易造成資料遺失、資源洩漏。本文實作 CFindKillProcess 類,遵循「禮貌請求 → 等待退出 → 強制終止」三階段策略,並正確處理 WM_QUERYENDSESSION、SendMessageTimeout (含 SMTO_NOTIMEOUTIFNOTHUNG)、主視窗枚舉等細節,程式碼可直接重用。

終止進程最佳實踐流程

  1. 發送 WM_QUERYENDSESSION:詢問視窗是否允許結束會話,模擬登出/關機流程,給 App 機會彈出「儲存變更?」對話框。
  2. 發送 WM_CLOSE:標準關閉請求,App 可在 OnClose 執行清理。
  3. WaitForSingleObject 等待進程結束:最多等待 5 秒(可調)。
  4. 最後手段 TerminateProcess:僅當上述失敗且呼叫者允許強制終止時執行。

關鍵 API 細節

API關鍵參數/旗標說明
SendMessageTimeoutSMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG若視窗未掛起則無限等待使用者回應對話框;掛起則立即返回。僅 Win2000/XP+ 支援。
PostMessage(WM_CLOSE)非同步發送避免死結,配合等待機制。
OpenProcessSYNCHRONIZE | PROCESS_TERMINATE取得同步與終止權限。
WaitForSingleObject逾時 5000ms等待進程物件發出訊號。

核心類別實作

1. 進程迭代器 CProcessIterator

class 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; }
};

2. 查找與終止類 CFindKillProcess

class CFindKillProcess {
public:
    DWORD FindProcess(LPCTSTR modname, BOOL bAddExe=TRUE); // 比對模組名 (含/不含 .exe)
    BOOL  KillProcess(DWORD pid, BOOL bZap);               // bZap=TRUE 允許 TerminateProcess
};

FindProcess 實作

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;
}

KillProcess 實作

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 — 整合上述,提供高層介面

適用範圍與注意

  • 範例為 Win2000 環境 (使用 SMTO_NOTIMEOUTIFNOTHUNG),Win9x 需調整旗標。
  • 需連結 Psapi.lib (EnumProcesses/GetModuleBaseName) 或動態載入。
  • 管理員權限可終止系統服務進程,一般使用者僅能終止同會話進程。

結語

「乾淨終止」不只是呼叫 TerminateProcess,而是尊重應用程式的關閉協議。CFindKillProcess 封裝了工業級終止邏輯,可直接整合至安裝程式、監控工具、自動化測試框架中。

本文配套原始碼含完整類別定義與命令列工具主程式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved