From 7118d87cbd372b9592cf3048b663f6678355d9ba Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Wed, 21 Jan 2026 19:16:29 -0600 Subject: [PATCH] Propagate foreground through the stack instead of using tricks --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 36 ++++++++----------- .../WindowsTerminal/WindowEmperor.cpp | 9 ++++- src/cascadia/wt/shim.cpp | 25 ++++++++++++- src/host/exe/Host.EXE.vcxproj | 1 + src/host/srvinit.cpp | 6 ++++ 5 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index e627bd4495..f56d1bff74 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -1457,31 +1457,23 @@ void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration, } else { - // Try first to send a message to the current foreground window. If it's not responding, it may - // be waiting on us to finish launching. Passing SMTO_NOTIMEOUTIFNOTHUNG means that we get the same - // behavior as before--that is, waiting for the message loop--but we've done an early return if - // it turns out that it was hung. - // SendMessageTimeoutW returns nonzero if it succeeds. - if (0 != SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0, SMTO_NOTIMEOUTIFNOTHUNG | SMTO_BLOCK | SMTO_ABORTIFHUNG, 1000, nullptr)) - { - const auto windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr); - const auto currentThreadId = GetCurrentThreadId(); + LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); + ShowWindow(_window.get(), SW_SHOW); - LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true)); - // Just in case, add the thread detach as a scope_exit, to make _sure_ we do it. - auto detachThread = wil::scope_exit([windowThreadProcessId, currentThreadId]() { - LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, false)); - }); - LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); - ShowWindow(_window.get(), SW_SHOW); + // Activate the window too. This will force us to the virtual desktop this + // window is on, if it's on another virtual desktop. + LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); - // Activate the window too. This will force us to the virtual desktop this - // window is on, if it's on another virtual desktop. - LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); + // Throw us on the active monitor. + _moveToMonitor(oldForegroundWindow, toMonitor); + } - // Throw us on the active monitor. - _moveToMonitor(oldForegroundWindow, toMonitor); - } + if (!SetForegroundWindow(_window.get())) + { + TraceLoggingWrite(g_hWindowsTerminalProvider, + "ActivateSetForegroundWindowFailed", + //TraceLoggingUInt64(static_cast(_window.get()), "hwnd"), + TraceLoggingWinError(GetLastError(), "error")); } } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 07e96fcc2e..78c46956d1 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -180,7 +180,14 @@ static wil::unique_mutex acquireMutexOrAttemptHandoff(const wchar_t* className, DWORD processId = 0; if (GetWindowThreadProcessId(hwnd, &processId) && processId) { - AllowSetForegroundWindow(processId); + if (!AllowSetForegroundWindow(processId)) + { + TraceLoggingWrite(g_hWindowsTerminalProvider, + "EmperorAllowSetForegroundWindowFailed", + //TraceLoggingUInt64(static_cast(hwnd), "hwnd"), + TraceLoggingPid(processId), + TraceLoggingWinError(GetLastError(), "error")); + } } if (SendMessageTimeoutW(hwnd, WM_COPYDATA, 0, reinterpret_cast(&cds), SMTO_ABORTIFHUNG | SMTO_ERRORONEXIT, 5000, nullptr)) diff --git a/src/cascadia/wt/shim.cpp b/src/cascadia/wt/shim.cpp index 0b00fcaf96..c6c38fb08b 100644 --- a/src/cascadia/wt/shim.cpp +++ b/src/cascadia/wt/shim.cpp @@ -6,10 +6,19 @@ #include #include #include +#include + +TRACELOGGING_DECLARE_PROVIDER(g_hShimProvider); +TRACELOGGING_DEFINE_PROVIDER( + g_hShimProvider, + "Microsoft.Windows.Terminal.Shim", + // tl:{d295502a-ab39-5565-c342-6e6d7659a422} + (0xd295502a, 0xab39, 0x5565, 0xc3, 0x42, 0x6e, 0x6d, 0x76, 0x59, 0xa4, 0x22)); #pragma warning(suppress : 26461) // we can't change the signature of wWinMain int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR pCmdLine, int) { + TraceLoggingRegister(g_hShimProvider); std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; // Cache our name (wt, wtd) @@ -32,5 +41,19 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR pCmdLine, int) // Go! wil::unique_process_information pi; - return !CreateProcessW(module.c_str(), cmdline.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); + if (!CreateProcessW(module.c_str(), cmdline.data(), nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)) + { + return 1; + } + + if (!AllowSetForegroundWindow(pi.dwProcessId)) + { + TraceLoggingWrite(g_hShimProvider, + "ShimAllowSetForegroundWindowFailed", + TraceLoggingPid(pi.dwProcessId, "processId"), + TraceLoggingWinError(GetLastError(), "error")); + } + + ResumeThread(pi.hThread); + return 0; } diff --git a/src/host/exe/Host.EXE.vcxproj b/src/host/exe/Host.EXE.vcxproj index 3deb6f37f3..6c21ade7a8 100644 --- a/src/host/exe/Host.EXE.vcxproj +++ b/src/host/exe/Host.EXE.vcxproj @@ -88,6 +88,7 @@ true winmm.lib;imm32.lib;%(AdditionalDependencies) + ext-ms-win-com-ole32-l1.dll icu.dll;%(DelayLoadDLLs) diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index cdb49db1bd..942865e89a 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -544,6 +544,12 @@ try myStartupInfo.wShowWindow = settings.GetShowWindow(); + if (IsApiSetImplemented("ext-ms-win-com-ole32-l1-1-1")) + { + HRESULT hr = CoAllowSetForegroundWindow(handoff.Get(), nullptr); + TraceLoggingWrite(g_hConhostV2EventTraceProvider, "PtyHandoffAllowSetForegroundWindow", TraceLoggingHResult(hr)); + } + wil::unique_handle inPipeOurSide; wil::unique_handle outPipeOurSide; RETURN_IF_FAILED(handoff->EstablishPtyHandoff(inPipeOurSide.addressof(),