using System; namespace Notify.Interop; /// /// 把目标窗口拉回前台 /// /// Windows 限制后台进程抢焦点,这里用一套组合技绕过:ALT 键模拟 + /// AttachThreadInput 把当前线程与前台/目标线程的输入队列挂接 + /// SetWindowPos/BringWindowToTop/SwitchToThisWindow/SetForegroundWindow 多管齐下 /// public static class WindowActivator { public static bool Activate(IntPtr hwnd) { if (hwnd == IntPtr.Zero || !Win32.IsWindow(hwnd)) { return false; } var root = Win32.GetAncestor(hwnd, Win32.GA_ROOT); if (root != IntPtr.Zero) { hwnd = root; } var foreground = Win32.GetForegroundWindow(); var curThread = Win32.GetCurrentThreadId(); var fgThread = Win32.GetWindowThreadProcessId(foreground, out _); var targetThread = Win32.GetWindowThreadProcessId(hwnd, out _); var attachedForeground = false; var attachedTarget = false; try { // 最小化的先还原 if (Win32.IsIconic(hwnd)) { Win32.ShowWindow(hwnd, Win32.SW_RESTORE); } // 模拟一次 ALT 抬起,满足 Windows 的"防焦点抢占"前置条件 Win32.keybd_event(Win32.VK_MENU, 0, 0, IntPtr.Zero); Win32.keybd_event(Win32.VK_MENU, 0, Win32.KEYEVENTF_KEYUP, IntPtr.Zero); if (fgThread != curThread) { attachedForeground = Win32.AttachThreadInput(curThread, fgThread, true); } if (targetThread != curThread && targetThread != fgThread) { attachedTarget = Win32.AttachThreadInput(curThread, targetThread, true); } Win32.AllowSetForegroundWindow(Win32.ASFW_ANY); Win32.SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, Win32.SWP_NOMOVE | Win32.SWP_NOSIZE | Win32.SWP_SHOWWINDOW); Win32.BringWindowToTop(hwnd); Win32.SwitchToThisWindow(hwnd, true); Win32.SetForegroundWindow(hwnd); Win32.ShowWindow(hwnd, Win32.SW_SHOW); if (Win32.GetForegroundWindow() != hwnd) { Win32.SetWindowPos(hwnd, Win32.HWND_TOPMOST, 0, 0, 0, 0, Win32.SWP_NOMOVE | Win32.SWP_NOSIZE | Win32.SWP_SHOWWINDOW); Win32.SetWindowPos(hwnd, Win32.HWND_NOTOPMOST, 0, 0, 0, 0, Win32.SWP_NOMOVE | Win32.SWP_NOSIZE | Win32.SWP_SHOWWINDOW); Win32.BringWindowToTop(hwnd); Win32.SetForegroundWindow(hwnd); } return Win32.GetAncestor(Win32.GetForegroundWindow(), Win32.GA_ROOT) == hwnd; } catch { return false; } finally { if (attachedTarget) { Win32.AttachThreadInput(curThread, targetThread, false); } if (attachedForeground) { Win32.AttachThreadInput(curThread, fgThread, false); } } } }