首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> C# 入門

C# 進程管理實戰:列舉系統進程、依視窗標題精確關閉指定實例

     編輯:C# 入門

前言:Process 類別與進程精確控制

System.Diagnostics.Process 提供系統進程列舉、模組資訊、記憶體使用、視窗標題等豐富屬性。本文示範從列舉所有進程、依名稱篩選、依主視窗標題精確識別並關閉特定實例的完整流程,解決「同名進程多實例」的精確控制問題。

基礎操作:列舉所有進程


using System.Diagnostics;

Process[] allProcesses = Process.GetProcesses();
foreach (Process p in allProcesses)
{
    Console.WriteLine($"PID: {p.Id,6} | Name: {p.ProcessName,-20} | Title: {p.MainWindowTitle}");
}

進階:依名稱取得進程陣列


// 取得所有名為 "notepad" 的進程
Process[] notepads = Process.GetProcessesByName("notepad");
foreach (Process p in notepads)
{
    Console.WriteLine($"PID: {p.Id} | Title: '{p.MainWindowTitle}' | Handle: 0x{p.MainWindowHandle:X}");
}

核心難點:同名進程多實例精確關閉

Process.GetProcessesByName 回傳同名進程陣列,若直接迴圈 Kill() 會關閉所有實例。需結合 MainWindowHandle 與 MainWindowTitle 精確定位。

完整實作:視窗標題選擇器


using System;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;

public class ProcessManager
{
    private ListBox lstProcessNames;
    private ListBox lstWindowTitles;
    private ArrayList windowHandles = new ArrayList();

    public void RefreshProcessList()
    {
        lstProcessNames.Items.Clear();
        Process[] processes = Process.GetProcesses();
        foreach (Process p in processes)
        {
            lstProcessNames.Items.Add(p.ProcessName);
        }
    }

    public void OnProcessSelected(string selectedProcessName)
    {
        lstWindowTitles.Items.Clear();
        windowHandles.Clear();
        
        Process[] processes = Process.GetProcessesByName(selectedProcessName);
        foreach (Process p in processes)
        {
            // 只顯示有主視窗的進程
            if (p.MainWindowHandle != IntPtr.Zero)
            {
                lstWindowTitles.Items.Add(p.MainWindowTitle);
                windowHandles.Add(p.MainWindowHandle);
            }
        }
    }

    public void CloseSelectedWindow(int selectedIndex)
    {
        if (selectedIndex < 0 || selectedIndex >= windowHandles.Count) return;
        
        IntPtr targetHandle = (IntPtr)windowHandles[selectedIndex];
        string targetTitle = lstWindowTitles.Items[selectedIndex].ToString();
        
        Process[] processes = Process.GetProcessesByName(lstProcessNames.SelectedItem.ToString());
        foreach (Process p in processes)
        {
            if (p.MainWindowHandle == targetHandle)
            {
                try
                {
                    p.Kill();
                    lstWindowTitles.Items.RemoveAt(selectedIndex);
                    windowHandles.RemoveAt(selectedIndex);
                    MessageBox.Show($"已關閉:{targetTitle}");
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"關閉失敗:{ex.Message}");
                }
                break;
            }
        }
    }
}

無主視窗進程 (服務/背景程式) 處理

MainWindowHandle 為 IntPtr.Zero 時無法用視窗標題識別,替代方案:

方法適用場景程式碼片段
PID 識別已知具體 PIDProcess.GetProcessById(pid)
命令列參數啟動參數不同WMI: new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process")
模組路徑同名但路徑不同p.MainModule.FileName (需權限)
啟動時間不同時段啟動p.StartTime
使用者帳戶不同使用者啟動WMI: GetOwner()

WMI 進階查詢:命令列與擁有者


using System.Management;

public static void ListProcessDetails()
{
    string query = "SELECT ProcessId, Name, CommandLine, ExecutablePath, CreationDate FROM Win32_Process";
    using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
    {
        foreach (ManagementObject obj in searcher.Get())
        {
            Console.WriteLine($"PID: {obj["ProcessId"]} | Name: {obj["Name"]}");
            Console.WriteLine($"  CmdLine: {obj["CommandLine"]}");
            Console.WriteLine($"  Path: {obj["ExecutablePath"]}");
            Console.WriteLine($"  Start: {ManagementDateTimeConverter.ToDateTime(obj["CreationDate"].ToString())}");
            
            // 取得擁有者
            string[] args = { "", "" };
            obj.InvokeMethod("GetOwner", args);
            Console.WriteLine($"  Owner: {args[0]}\\{args[1]}");
        }
    }
}

權限與安全性考量

  • 管理員權限:關閉系統進程、存取其他使用者進程需要提權
  • app.manifest 設定
  • 防毒/EDR 攔截:Kill() 可能被安全軟體攔截,需例外處理
  • 資料遺失風險:強制關閉可能導致未儲存資料遺失,建議先嘗試 CloseMainWindow() 發送關閉訊息

優雅關閉 vs 強制終止


// 1. 優雅關閉:發送 WM_CLOSE 訊息,讓程式自行處理儲存確認
if (p.CloseMainWindow())
{
    // 等待結束
    p.WaitForExit(5000);
    if (!p.HasExited) p.Kill(); // 逾時再強制
}

// 2. 直接強制終止
p.Kill();

完整 WinForms 管理工具介面


public class ProcessManagerForm : Form
{
    private ListBox lstProcesses, lstWindows;
    private Button btnRefresh, btnClose;
    private ArrayList handles = new ArrayList();

    public ProcessManagerForm()
    {
        // UI 初始化...
        btnRefresh.Click += (s,e) => LoadProcesses();
        lstProcesses.SelectedIndexChanged += (s,e) => LoadWindows();
        btnClose.Click += (s,e) => CloseWindow();
    }

    void LoadProcesses()
    {
        lstProcesses.Items.Clear();
        foreach (Process p in Process.GetProcesses())
            lstProcesses.Items.Add(p.ProcessName);
    }

    void LoadWindows()
    {
        lstWindows.Items.Clear(); handles.Clear();
        if (lstProcesses.SelectedItem == null) return;
        
        foreach (Process p in Process.GetProcessesByName(lstProcesses.SelectedItem.ToString()))
        {
            if (p.MainWindowHandle != IntPtr.Zero)
            {
                lstWindows.Items.Add($"[{p.Id}] {p.MainWindowTitle}");
                handles.Add(p.MainWindowHandle);
            }
        }
    }

    void CloseWindow()
    {
        if (lstWindows.SelectedIndex < 0) return;
        IntPtr h = (IntPtr)handles[lstWindows.SelectedIndex];
        foreach (Process p in Process.GetProcessesByName(lstProcesses.SelectedItem.ToString()))
        {
            if (p.MainWindowHandle == h)
            {
                p.CloseMainWindow(); // 優雅關閉
                if (!p.WaitForExit(3000)) p.Kill();
                break;
            }
        }
        LoadWindows();
    }
}

結語

C# 進程管理的核心在於 Process.GetProcessesByName 取得同名進程群組,結合 MainWindowHandle 與 MainWindowTitle 實現實例級精確識別。無視窗進程需改用 WMI 查詢命令列、模組路徑、擁有者等屬性區分。實務開發中應優先嘗試 CloseMainWindow() 優雅關閉,失敗再回退 Kill(),並妥善處理權限與安全軟體攔截問題。

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