首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

C# 關閉其他程式視窗與進程的兩大核心技術深度解析

     編輯:更多程式設計知識

在系統整合、自動化測試、監控工具開發中,經常需要以程式控制方式關閉其他應用程式。本文深度解析兩大核心技術:進程級終止與視窗訊息發送,提供完整程式碼與適用場景分析。

方法一:進程級終止 - 徹底但較暴力

透過 System.Diagnostics.Process 列舉並終止目標進程:


/// 
/// 依進程名稱關閉程式(優雅嘗試後強制終止)
/// 
/// 進程名稱(不含 .exe)
/// 是否成功關閉至少一個進程
public static bool CloseProcessByName(string processName) {
    bool anyClosed = false;
    
    foreach (Process proc in Process.GetProcessesByName(processName)) {
        try {
            // 1. 先嘗試優雅關閉(發送 WM_CLOSE)
            if (proc.CloseMainWindow()) {
                // 等待進程自行結束(最多 3 秒)
                if (proc.WaitForExit(3000)) {
                    anyClosed = true;
                    continue;
                }
            }
            
            // 2. 優雅關閉失效,強制終止
            proc.Kill();
            proc.WaitForExit(1000);
            anyClosed = true;
            
        } catch (Exception ex) {
            // 記錄錯誤:權限不足、進程已結束等
            LogError($"關閉進程 {proc.ProcessName} (PID:{proc.Id}) 失敗: {ex.Message}");
        } finally {
            proc.Dispose();
        }
    }
    
    return anyClosed;
}

進階:取得進程清單供使用者選擇


public static List GetRunningProcesses() {
    return Process.GetProcesses()
        .Where(p => !string.IsNullOrEmpty(p.MainWindowTitle)) // 有視窗的進程
        .Select(p => new ProcessInfo {
            Id = p.Id,
            Name = p.ProcessName,
            Title = p.MainWindowTitle,
            Path = GetProcessPath(p)
        })
        .OrderBy(p => p.Name)
        .ToList();
}

public class ProcessInfo {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Title { get; set; }
    public string Path { get; set; }
}

方法二:視窗訊息發送 - 精準優雅

透過 P/Invoke 呼叫 Windows API 發送 WM_CLOSE 訊息:


using System.Runtime.InteropServices;

public static class WindowCloser {
    // Windows API 宣告
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    
    [DllImport("user32.dll")]
    private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    
    // 常數
    private const uint WM_CLOSE = 0x0010;
    private const uint WM_QUIT = 0x0012;
    
    /// 
    /// 依視窗標題關閉視窗(優雅關閉)
    /// 
    public static bool CloseWindowByTitle(string windowTitle) {
        IntPtr hWnd = FindWindow(null, windowTitle);
        if (hWnd == IntPtr.Zero) return false;
        
        // 發送關閉訊息(非同步,不阻塞)
        return PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    }
    
    /// 
    /// 依視窗類別名稱關閉
    /// 
    public static bool CloseWindowByClass(string className) {
        IntPtr hWnd = FindWindow(className, null);
        if (hWnd == IntPtr.Zero) return false;
        
        return PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
    }
    
    /// 
    /// 列舉所有視窗並依條件關閉
    /// 
    public static int CloseWindowsMatching(Func titleMatcher) {
        int count = 0;
        EnumWindows((hWnd, lParam) => {
            if (IsWindowVisible(hWnd)) {
                StringBuilder sb = new StringBuilder(256);
                GetWindowText(hWnd, sb, sb.Capacity);
                string title = sb.ToString();
                
                if (titleMatcher(title)) {
                    PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
                    count++;
                }
            }
            return true; // 繼續列舉
        }, IntPtr.Zero);
        return count;
    }
    
    // 列舉視窗所需 API
    private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
    [DllImport("user32.dll")]
    private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
    
    [DllImport("user32.dll")]
    private static extern bool IsWindowVisible(IntPtr hWnd);
    
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
}

兩大方法深度比較

維度進程終止 (Process.Kill)視窗訊息 (WM_CLOSE)
優雅程度強制立即終止,無清理機會觸發 FormClosing,可取消、可存檔
精準度依進程名稱,可能誤殺同名進程依視窗標題/類別,精準鎖定單一視窗
權限需求需同等或更高權限通常同使用者權限即可
無視窗進程可關閉服務、背景進程無效(需視窗控制代碼)
跨 Session受限於 Session 隔離受限於 UIPI (User Interface Privilege Isolation)

實戰建議:混合策略


public static bool SmartClose(string target) {
    // 1. 嘗試視窗訊息(優雅)
    if (WindowCloser.CloseWindowByTitle(target)) {
        // 等待確認關閉
        if (WaitForProcessExit(target, 5000)) return true;
    }
    
    // 2. 嘗試進程名稱關閉
    if (CloseProcessByName(target)) return true;
    
    // 3. 最後手段:模糊比對視窗標題
    return WindowCloser.CloseWindowsMatching(t => t.Contains(target)) > 0;
}

權限與安全性注意事項

  • UIPI 限制:低完整性進程無法向高完整性視窗發送訊息,需在 Manifest 宣告 requireAdministrator
  • 防毒軟體攔截:進程終止行為常被監控,建議數位簽章
  • 系統關鍵進程:絕勿嘗試關閉 csrss.exe、winlogon.exe 等系統進程
  • 資料完整性:優先使用優雅關閉,避免使用者資料遺失

掌握這兩大核心技術並靈活組合,能應對絕大多數「關閉其他程式」的實際開發需求。

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