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

C++ Builder 列舉系統進程並強制終止:ToolHelp32 API 完整實作

     編輯:關於C++

專案目標:建立進程管理工具核心功能

  • 列舉所有執行中進程 (名稱、PID、父進程 ID、版本資訊、公司名稱、檔案描述)。
  • 在 ListView 顯示,支援選擇進程終止。
  • 終止時針對**父進程 ID** (而非僅 PID) 發送終止信號,避免僅殺掉子進程導致殭屍進程殘留。

核心 API:ToolHelp32 Snapshot

CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 取得系統進程快照,配合 Process32First/Next 遍歷 PROCESSENTRY32 結構。優點:支援 Win9x~Win11、無需 WMI/PDH 額外相依、速度快。

完整實作程式碼 (C++ Builder VCL)

標頭檔宣告 (Unit1.h)


#include 
#include  // ToolHelp32 API
#pragma hdrstop
#include "Unit1.h"

#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

列舉進程並填充 ListView (按鈕/重新整理事件)


void __fastcall TForm1::RefreshProcessList() {
  ListView1->Items->Clear();
  ListView1->Items->BeginUpdate();
  try {
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnap == INVALID_HANDLE_VALUE) {
      ShowMessage("建立快照失敗: " + IntToStr(GetLastError()));
      return;
    }

    PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
    if (Process32First(hSnap, &pe)) {
      do {
        TListItem* item = ListView1->Items->Add();
        item->Caption = pe.szExeFile; // 進程名稱
        item->SubItems->Add(IntToStr(pe.th32ProcessID)); // PID
        item->SubItems->Add(IntToStr(pe.th32ParentProcessID)); // 父 PID
        item->SubItems->Add(IntToStr(pe.cntThreads)); // 執行緒數
        item->SubItems->Add(IntToStr(pe.pcPriClassBase)); // 基礎優先權

        // 取得檔案版本資訊 (選擇性)
        AnsiString exePath = GetModuleFileNameExForPID(pe.th32ProcessID); // 需自行實作或用 PSAPI
        if (!exePath.IsEmpty()) {
          DWORD dwHandle, dwSize = GetFileVersionInfoSize(exePath.c_str(), &dwHandle);
          if (dwSize) {
            std::vector buf(dwSize);
            if (GetFileVersionInfo(exePath.c_str(), 0, dwSize, buf.data())) {
              UINT len; void* pVal;
              // 語言/代碼頁通常為 040904B0 (英文美語) 或 080404B0 (中文繁體)
              // 實務應先查詢 VarFileInfo\Translation 取得正確語言代碼
              char subBlock[128];
              wsprintf(subBlock, "\\StringFileInfo\\040904B0\\FileVersion");
              if (VerQueryValue(buf.data(), subBlock, &pVal, &len))
                item->SubItems->Add(AnsiString((char*)pVal));
              wsprintf(subBlock, "\\StringFileInfo\\040904B0\\CompanyName");
              if (VerQueryValue(buf.data(), subBlock, &pVal, &len))
                item->SubItems->Add(AnsiString((char*)pVal));
              wsprintf(subBlock, "\\StringFileInfo\\040904B0\\FileDescription");
              if (VerQueryValue(buf.data(), subBlock, &pVal, &len))
                item->SubItems->Add(AnsiString((char*)pVal));
            }
          }
        }
      } while (Process32Next(hSnap, &pe));
    }
    CloseHandle(hSnap);
  } __finally {
    ListView1->Items->EndUpdate();
  }
}

輔助函式:依 PID 取得完整執行檔路徑 (PSAPI)


#include 
#pragma comment(lib, "psapi.lib")

AnsiString GetModuleFileNameExForPID(DWORD pid) {
  HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid);
  if (!hProc) return "";
  char path[MAX_PATH * 2] = {0};
  DWORD len = GetModuleFileNameEx(hProc, NULL, path, sizeof(path));
  CloseHandle(hProc);
  return (len > 0) ? AnsiString(path) : "";
}

終止選定進程 (按鈕事件)


void __fastcall TForm1::BtnTerminateClick(TObject *Sender) {
  if (ListView1->Selected == NULL) {
    ShowMessage("請先選擇要終止的進程");
    return;
  }

  // 取得父進程 ID (欄位索引 2)
  DWORD parentPID = StrToIntDef(ListView1->Selected->SubItems->Strings[1], 0);
  if (parentPID == 0) {
    ShowMessage("無效的父進程 ID");
    return;
  }

  // 確認對話框
  if (Application->MessageBox(
        ("確定要終止 PID " + IntToStr(parentPID) + " 及其子進程嗎?").c_str(),
        "確認終止", MB_YESNO | MB_ICONWARNING) != IDYES)
    return;

  // 開啟進程需 TERMINATE 權限
  HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, parentPID);
  if (!hProc) {
    ShowMessage("OpenProcess 失敗,錯誤碼: " + IntToStr(GetLastError()) + 
                " (可能權限不足,請以管理員身分執行)");
    return;
  }

  if (TerminateProcess(hProc, -1)) {
    ShowMessage("進程終止信號已發送 (退出碼 -1)");
  } else {
    ShowMessage("TerminateProcess 失敗,錯誤碼: " + IntToStr(GetLastError()));
  }
  CloseHandle(hProc);

  // 重新整理列表
  RefreshProcessList();
}

關鍵技術細節與最佳實務

1. 為什麼要終止父進程而非子進程?

  • 某些應用程式 (Chrome、Edge、Visual Studio、防毒軟體) 採用**多進程架構**:主進程管理多個渲染/工作者子進程。
  • 若僅終止子進程 (PID),主進程可能偵測到崩潰並**自動重啟**子進程,或進入異常狀態。
  • 終止主進程 (父 PID) 通常會觸發其正常關閉流程 (或被強制終止),帶走所有子進程。
  • 本範例 ListView 顯示 th32ParentProcessID,終止時使用該值。

2. 權限與 UAC

  • OpenProcess(PROCESS_TERMINATE) 需目標進程 DACL 允許。系統進程 (csrss.exe, services.exe, lsass.exe)、高完整性進程 (管理員執行) 預設拒絕一般使用者終止。
  • 解決方案:應用程式 Manifest 設定 requireAdministrator,或以「系統管理員身分執行」。
  • 極端情況 (受保護進程 PPL, Protected Process Light):需簽署驅動程式或利用 SeDebugPrivilege (參考文章 7850 EnableDebugPriv)。

3. 版本資訊語言代碼問題

  • VerQueryValue 的 \\StringFileInfo\\LangCodePage\\Key 中 LangCodePage 為 4 位元組語言 ID + 4 位元組代碼頁 (十六進位)。
  • 常見:040904B0 (英文/1200), 080404B0 (中文繁體/1200), 040404E4 (中文簡體/936)。
  • **正確做法**:先查詢 \\VarFileInfo\\Translation 取得陣列,逐一嘗試,而非硬編碼。

4. 替代方案:WMI / PSAPI EnumProcesses

  • PSAPI EnumProcesses:僅取得 PID 陣列,需再呼叫 OpenProcess + GetModuleFileNameEx 取得名稱,較慢但無 ToolHelp32 相依 (NT 4.0 SP4+ 內建)。
  • WMI (Win32_Process):SELECT ProcessId, Name, ExecutablePath, ParentProcessId, CommandLine FROM Win32_Process。功能最強 (可取得命令列參數)、跨機器查詢,但需 COM 初始化、速度較慢、依賴 WMI 服務。
  • NtQuerySystemInformation (SystemProcessInformation):核心層級、最完整資訊 (含執行緒、記憶體、控制代碼計數),但未文件化、結構版本相容性風險高。

介面設計建議 (VCL)

  • TListView 設定 ViewStyle = vsReport, ReadOnly = true, RowSelect = true, FullRowSelect = true。
  • 欄位:名稱 (200px), PID (80px), 父PID (80px), 執行緒 (60px), 優先權 (60px), 版本 (100px), 公司 (150px), 描述 (200px)。
  • 加入 TTimer 間隔 3~5 秒自動 RefreshProcessList,或提供手動重新整理按鈕。
  • 右鍵選單:終止進程、開啟檔案位置、屬性、設定優先權、建立 Dump 檔 (MiniDumpWriteDump)。

安全性與道德提醒

  1. 進程終止為**破壞性操作**,可能導致資料遺失、系統不穩、服務中斷。
  2. 僅用於:合法授權的系統管理、開發除錯、惡意軟體清理 (配合掃毒引擎)、資源回收腳本。
  3. 企業環境部署前,需經資安審核、記錄稽核日誌、限定操作人員權限 (RBAC)。
  4. 提供「排除清單」保護關鍵進程 (系統核心、防毒、備份代理、資料庫引擎) 防誤殺。

結語

ToolHelp32 API 提供了輕量、高效、跨版本的進程列舉能力。結合 PSAPI 取得完整路徑與版本資訊,並針對父進程 ID 實施終止策略,即可構建實用的進程管理工具核心。務必嚴守權限最小化、操作可審計、關鍵進程受保護三大原則,確保工具在實際部署中安全可控。

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