WPF应用中检测IE浏览器、文件资源管理器等特定窗口关闭状态的问题排查与优化咨询
Absolutely, your current implementation needs adjustments to handle these edge cases with IE and File Explorer—both have unique process/window behaviors that break the simple Process.Exited approach. Let's break down the problems and fix them step by step:
Why Your Current Code Fails
1. Multiple IE Windows Require Full Exit
IE (and legacy Edge in IE mode) uses a shared process model for multiple windows. When you launch IE once, you're starting a parent process that spawns child windows but often keeps the parent alive until all windows close. Your code only listens to the parent process's Exited event, so the button won't disappear until every IE window is closed.
2. File Explorer Window Close Isn't Detected
File Explorer (explorer.exe) is a system shell process—closing a single window doesn't terminate the process (it keeps running to handle the desktop, taskbar, etc.). The Process.Exited event will never fire here, since the process stays alive.
Solution: Listen to Window Destruction Instead of Process Exit
The reliable way to detect when a specific program window is closed is to monitor the window's destruction event using Windows API hooks, rather than relying on process lifecycle. Here's how to modify your RunningProgram class:
Step 1: Add Required Windows API Imports
Add these to your RunningProgram class to hook into window events:
[DllImport("user32.dll")] private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); [DllImport("user32.dll")] private static extern bool UnhookWinEvent(IntPtr hWinEventHook); private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime); private const uint EVENT_OBJECT_DESTROY = 0x8001; private const uint WINEVENT_OUTOFCONTEXT = 0x0000; private IntPtr _winEventHook = IntPtr.Zero;
Step 2: Replace Process.Exited with Window Destruction Hook
Modify your constructor to remove the process exit listener and set up the window hook instead:
public RunningProgram(IntPtr programHandle, string programName, Thickness margin, string imagePath, Process programProcess) { this.ProgramHandle = programHandle; this.ProgramName = programName; this.Margin = margin; this.Background = SetButtonBackground(imagePath); this.Width = 70; this.Height = 70; this.Click += ShowWindow_Click; this.BorderBrush = Brushes.Transparent; this.ProgramProcess = programProcess; // Remove old process exit listener // this.ProgramProcess.EnableRaisingEvents = true; // this.ProgramProcess.Exited += (sender, e) => { RemoveRunningProgram((Process)sender); }; // Set up hook to listen for window destruction _winEventHook = SetWinEventHook( EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY, IntPtr.Zero, new WinEventDelegate(WinEventProc), (uint)programProcess.Id, 0, WINEVENT_OUTOFCONTEXT ); }
Step 3: Implement the Window Destruction Handler
Add this method to respond when the target window is closed:
private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { // Only react to the destruction of our target window (ignore child objects) if (eventType == EVENT_OBJECT_DESTROY && hwnd == ProgramHandle && idObject == 0 && idChild == 0) { // Remove the taskbar button RemoveRunningProgram(null); // Clean up the hook to avoid memory leaks UnhookWinEvent(hWinEventHook); _winEventHook = IntPtr.Zero; } }
Step 4: Clean Up Hooks When the Button Is Disposed
Override the Dispose method to ensure hooks are cleaned up if the button is removed before the window closes:
protected override void Dispose(bool disposing) { base.Dispose(disposing); if (_winEventHook != IntPtr.Zero) { UnhookWinEvent(_winEventHook); _winEventHook = IntPtr.Zero; } }
Step 5: Ensure You Get a Valid Window Handle
When launching programs, sometimes the main window handle isn't available immediately. Update your process launch code to wait for the window to be created:
Process process = Process.Start("iexplore.exe"); process.WaitForInputIdle(); int maxAttempts = 10; int attempts = 0; // Wait until we get a valid main window handle while (process.MainWindowHandle == IntPtr.Zero && attempts < maxAttempts) { Thread.Sleep(100); process.Refresh(); attempts++; } // Create the RunningProgram button with the valid handle var runningProgram = new RunningProgram(process.MainWindowHandle, "IE", new Thickness(5), "ie-icon.png", process); TaskbarWindow.RunningPrograms.Add(runningProgram);
Key Improvements
- IE Window Detection: The button will disappear as soon as the specific IE window you launched is closed (no need to wait for all windows to close).
- File Explorer Detection: You'll immediately detect when the File Explorer window is closed, since we're listening directly to the window's destruction event.
- No More Delays: Avoids waiting for a process that never exits (like
explorer.exe).
内容的提问来源于stack exchange,提问作者ConfusedAndBald

