在系統整合、自動化測試、監控工具開發中,經常需要以程式控制方式關閉其他應用程式。本文深度解析兩大核心技術:進程級終止與視窗訊息發送,提供完整程式碼與適用場景分析。
透過 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;
}
requireAdministrator掌握這兩大核心技術並靈活組合,能應對絕大多數「關閉其他程式」的實際開發需求。