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

進程管理利器:枚舉、篩選與精準終止非自身進程

     編輯:VC 教學

核心需求

開發任務管理器、安全防護軟件或系統優化工具時,常需:

  1. 枚舉系統所有進程
  2. 獲取進程詳細信息(路徑、版本、簽名、父進程)
  3. 精準終止指定進程(避免誤殺關鍵系統進程)
  4. 排除自身進程

ToolHelp32 快照機制詳解

Windows 提供 tlhelp32.h API,通過快照機制獲取進程靜態視圖:

HANDLE CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID);
BOOL Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
BOOL Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

PROCESSENTRY32 關鍵字段:

字段含義
th32ProcessID進程 PID
th32ParentProcessID父進程 PID(構建進程樹關鍵)
szExeFile可執行文件名(MAX_PATH)
cntThreads線程數
pcPriClassBase優先級基值

完整枚舉與信息豐富化(C++ Builder/Delphi 風格)

#include 
#include 

void EnumerateProcesses(TListView* listView) {
    listView->Items->Clear();
    
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) return;

    PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
    if (!Process32First(hSnapshot, &pe32)) {
        CloseHandle(hSnapshot);
        return;
    }

    do {
        TListItem* item = listView->Items->Add();
        item->Caption = pe32.szExeFile;
        item->SubItems->Add(IntToStr(pe32.th32ParentProcessID));
        item->SubItems->Add(IntToHex(pe32.th32ProcessID, 8).UpperCase());

        // 獲取文件版本信息
        String exePath = GetFullPath(pe32.szExeFile); // 需實現路徑搜索邏輯
        if (FileExists(exePath)) {
            DWORD handle, size = GetFileVersionInfoSize(exePath.c_str(), &handle);
            if (size > 0) {
                std::vector buffer(size);
                if (GetFileVersionInfo(exePath.c_str(), 0, size, buffer.data())) {
                    UINT len;
                    LPVOID ptr;
                    // 文件版本
                    if (VerQueryValue(buffer.data(), L"\\StringFileInfo\\040904E4\\FileVersion", &ptr, &len))
                        item->SubItems->Add((wchar_t*)ptr);
                    // 公司名
                    if (VerQueryValue(buffer.data(), L"\\StringFileInfo\\040904E4\\CompanyName", &ptr, &len))
                        item->SubItems->Add((wchar_t*)ptr);
                    // 文件描述
                    if (VerQueryValue(buffer.data(), L"\\StringFileInfo\\040904E4\\FileDescription", &ptr, &len))
                        item->SubItems->Add((wchar_t*)ptr);
                }
            }
        }
    } while (Process32Next(hSnapshot, &pe32));

    CloseHandle(hSnapshot);
}

精準終止進程:父進程 PID 策略

直接終止子進程可能導致殭屍進程或資源洩漏。正確做法:

  1. 獲取目標進程的 父進程 PID(th32ParentProcessID)
  2. 打開父進程句柄:OpenProcess(PROCESS_TERMINATE, FALSE, parentPID)
  3. 終止父進程:TerminateProcess(hProcess, EXIT_CODE)
bool TerminateProcessTree(DWORD targetPID) {
    // 1. 獲取父進程 PID(實際場景中可能需遞歸查找頂層父進程)
    DWORD parentPID = GetParentPID(targetPID); // 從快照中查找
    
    // 2. 打開父進程
    HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, parentPID);
    if (!hProcess) return false;

    // 3. 終止
    BOOL result = TerminateProcess(hProcess, 0xDEAD);
    CloseHandle(hProcess);
    return result;
}

安全防護:關鍵進程白名單

終止前必須檢查白名單,防止誤殺導致系統崩潰:

const wchar_t* CRITICAL_PROCESSES[] = {
    L"csrss.exe", L"wininit.exe", L"services.exe", L"lsass.exe",
    L"smss.exe", L"winlogon.exe", L"svchost.exe", L"explorer.exe",
    L"System", L"Registry", L"fontdrvhost.exe", L"dwm.exe"
};

bool IsCriticalProcess(const wchar_t* exeName) {
    for (auto critical : CRITICAL_PROCESSES) {
        if (_wcsicmp(exeName, critical) == 0) return true;
    }
    return false;
}

C# 現代實現(System.Diagnostics)

using System.Diagnostics;
using System.Linq;

public class ProcessManager {
    public ProcessInfo[] GetAllProcesses() {
        return Process.GetProcesses()
            .Where(p => !IsCriticalProcess(p.ProcessName))
            .Select(p => new ProcessInfo {
                Id = p.Id,
                Name = p.ProcessName,
                MainModulePath = GetModulePathSafe(p),
                ParentId = GetParentProcessId(p.Id),
                FileVersion = GetFileVersion(p),
                Company = GetCompanyName(p),
                Description = GetDescription(p)
            }).ToArray();
    }

    public bool KillProcessTree(int pid) {
        var parent = Process.GetProcessById(pid);
        if (IsCriticalProcess(parent.ProcessName)) return false;
        
        try {
            parent.Kill(); // 會遞歸終止子進程
            parent.WaitForExit(5000);
            return true;
        } catch {
            return false;
        }
    }

    private string GetModulePathSafe(Process p) {
        try { return p.MainModule.FileName; } catch { return "Access Denied"; }
    }
}

工程化建議

  1. 權限提升:清單配置 requireAdministrator,或使用 OpenProcessToken + AdjustTokenPrivileges 啟用 SE_DEBUG_PRIVILEGE
  2. 進程樹構建:父子關係可能因父進程退出而變更(重新父進程為 explorer.exe 或 services.exe),需實時刷新快照
  3. 異步非阻塞:枚舉耗時操作放入後台線程,UI 綁定 ObservableCollection 或 VirtualizingStackPanel
  4. 日誌審計:記錄每次終止操作:操作者、目標進程、時間、結果、調用堆棧

掌握進程枚舉與終止的核心邏輯,是構建系統級工具、EDR Agent、自動化測試框架的基石。

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