diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 013bd8b8a7..f9739216ff 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2183,6 +2183,7 @@ wnd WNDALLOC WNDCLASS WNDCLASSEX +WNDCLASSEXW WNDCLASSW Wndproc WNegative diff --git a/src/interactivity/base/InteractivityFactory.cpp b/src/interactivity/base/InteractivityFactory.cpp index 4294ef5981..3fd2b63685 100644 --- a/src/interactivity/base/InteractivityFactory.cpp +++ b/src/interactivity/base/InteractivityFactory.cpp @@ -305,14 +305,16 @@ using namespace Microsoft::Console::Interactivity; try { static const auto PSEUDO_WINDOW_CLASS = L"PseudoConsoleWindow"; - WNDCLASS pseudoClass{ 0 }; + WNDCLASSEXW pseudoClass{ 0 }; switch (level) { case ApiLevel::Win32: { + pseudoClass.cbSize = sizeof(WNDCLASSEXW); pseudoClass.lpszClassName = PSEUDO_WINDOW_CLASS; pseudoClass.lpfnWndProc = s_PseudoWindowProc; - RegisterClass(&pseudoClass); + pseudoClass.cbWndExtra = GWL_CONSOLE_WNDALLOC; // this is required to store the owning thread/process override in NTUSER + auto windowClassAtom{ RegisterClassExW(&pseudoClass) }; // Note that because we're not specifying WS_CHILD, this window // will become an _owned_ window, not a _child_ window. This is @@ -333,7 +335,7 @@ using namespace Microsoft::Console::Interactivity; // Attempt to create window. hwnd = CreateWindowExW(exStyles, - PSEUDO_WINDOW_CLASS, + reinterpret_cast(windowClassAtom), nullptr, windowStyle, 0, diff --git a/src/interactivity/base/RemoteConsoleControl.cpp b/src/interactivity/base/RemoteConsoleControl.cpp index 7cd03e3631..28823e22b7 100644 --- a/src/interactivity/base/RemoteConsoleControl.cpp +++ b/src/interactivity/base/RemoteConsoleControl.cpp @@ -74,4 +74,11 @@ template return _SendTypedPacket(_pipe.get(), HostSignals::EndTask, data); } +[[nodiscard]] NTSTATUS RemoteConsoleControl::SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) +{ + // This call doesn't need to get forwarded to the root conhost. Just handle + // it in-proc, to set the owner of OpenConsole + return _control.SetWindowOwner(hwnd, processId, threadId); +} + #pragma endregion diff --git a/src/interactivity/base/RemoteConsoleControl.hpp b/src/interactivity/base/RemoteConsoleControl.hpp index b922b9beaa..902052678e 100644 --- a/src/interactivity/base/RemoteConsoleControl.hpp +++ b/src/interactivity/base/RemoteConsoleControl.hpp @@ -27,6 +27,7 @@ namespace Microsoft::Console::Interactivity [[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId); [[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground); [[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags); + [[nodiscard]] NTSTATUS SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId); private: wil::unique_handle _pipe; diff --git a/src/interactivity/inc/IConsoleControl.hpp b/src/interactivity/inc/IConsoleControl.hpp index 7129cff68f..66fccee661 100644 --- a/src/interactivity/inc/IConsoleControl.hpp +++ b/src/interactivity/inc/IConsoleControl.hpp @@ -24,5 +24,6 @@ namespace Microsoft::Console::Interactivity [[nodiscard]] virtual NTSTATUS NotifyConsoleApplication(DWORD dwProcessId) = 0; [[nodiscard]] virtual NTSTATUS SetForeground(HANDLE hProcess, BOOL fForeground) = 0; [[nodiscard]] virtual NTSTATUS EndTask(DWORD dwProcessId, DWORD dwEventType, ULONG ulCtrlFlags) = 0; + [[nodiscard]] virtual NTSTATUS SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) = 0; }; } diff --git a/src/interactivity/onecore/ConsoleControl.cpp b/src/interactivity/onecore/ConsoleControl.cpp index 248d02b3ac..7841bfbe19 100644 --- a/src/interactivity/onecore/ConsoleControl.cpp +++ b/src/interactivity/onecore/ConsoleControl.cpp @@ -38,4 +38,9 @@ using namespace Microsoft::Console::Interactivity::OneCore; sizeof(*a)); } +[[nodiscard]] NTSTATUS ConsoleControl::SetWindowOwner(HWND, DWORD, DWORD) noexcept +{ + return STATUS_SUCCESS; +} + #pragma endregion diff --git a/src/interactivity/onecore/ConsoleControl.hpp b/src/interactivity/onecore/ConsoleControl.hpp index 4771c6c222..4b917a2f15 100644 --- a/src/interactivity/onecore/ConsoleControl.hpp +++ b/src/interactivity/onecore/ConsoleControl.hpp @@ -27,5 +27,6 @@ namespace Microsoft::Console::Interactivity::OneCore [[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId) noexcept override; [[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) noexcept override; [[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) override; + [[nodiscard]] NTSTATUS SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept override; }; } diff --git a/src/interactivity/win32/ConsoleControl.cpp b/src/interactivity/win32/ConsoleControl.cpp index 78e66110df..833f36b1cd 100644 --- a/src/interactivity/win32/ConsoleControl.cpp +++ b/src/interactivity/win32/ConsoleControl.cpp @@ -52,6 +52,18 @@ using namespace Microsoft::Console::Interactivity::Win32; sizeof(ConsoleEndTaskParams)); } +[[nodiscard]] NTSTATUS ConsoleControl::SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept +{ + CONSOLEWINDOWOWNER ConsoleOwner; + ConsoleOwner.hwnd = hwnd; + ConsoleOwner.ProcessId = processId; + ConsoleOwner.ThreadId = threadId; + + return Control(ConsoleControl::ControlType::ConsoleSetWindowOwner, + &ConsoleOwner, + sizeof(ConsoleOwner)); +} + #pragma endregion #pragma region Public Methods diff --git a/src/interactivity/win32/ConsoleControl.hpp b/src/interactivity/win32/ConsoleControl.hpp index fdd490a528..1e9efe6c59 100644 --- a/src/interactivity/win32/ConsoleControl.hpp +++ b/src/interactivity/win32/ConsoleControl.hpp @@ -48,6 +48,7 @@ namespace Microsoft::Console::Interactivity::Win32 [[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId); [[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground); [[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags); + [[nodiscard]] NTSTATUS SetWindowOwner(HWND hwnd, DWORD processId, DWORD threadId) noexcept override; // Public Members [[nodiscard]] NTSTATUS Control(_In_ ConsoleControl::ControlType ConsoleCommand, diff --git a/src/interactivity/win32/windowio.cpp b/src/interactivity/win32/windowio.cpp index 559981a3c2..a92c3da140 100644 --- a/src/interactivity/win32/windowio.cpp +++ b/src/interactivity/win32/windowio.cpp @@ -78,7 +78,13 @@ VOID SetConsoleWindowOwner(const HWND hwnd, _Inout_opt_ ConsoleProcessHandle* pP else { // Find a process to own the console window. If there are none then let's use conhost's. - pProcessData = gci.ProcessHandleList.GetFirstProcess(); + pProcessData = gci.ProcessHandleList.FindProcessInList(ConsoleProcessList::ROOT_PROCESS_ID); + if (!pProcessData) + { + // No root process ID? Pick the oldest existing process. + pProcessData = gci.ProcessHandleList.GetFirstProcess(); + } + if (pProcessData != nullptr) { dwProcessId = pProcessData->dwProcessId; @@ -92,16 +98,8 @@ VOID SetConsoleWindowOwner(const HWND hwnd, _Inout_opt_ ConsoleProcessHandle* pP } } - CONSOLEWINDOWOWNER ConsoleOwner; - ConsoleOwner.hwnd = hwnd; - ConsoleOwner.ProcessId = dwProcessId; - ConsoleOwner.ThreadId = dwThreadId; - // Comment out this line to enable UIA tree to be visible until UIAutomationCore.dll can support our scenario. - LOG_IF_FAILED(ServiceLocator::LocateConsoleControl() - ->Control(ConsoleControl::ControlType::ConsoleSetWindowOwner, - &ConsoleOwner, - sizeof(ConsoleOwner))); + LOG_IF_NTSTATUS_FAILED(ServiceLocator::LocateConsoleControl()->SetWindowOwner(hwnd, dwProcessId, dwThreadId)); } // ---------------------------- @@ -1048,6 +1046,10 @@ DWORD WINAPI ConsoleInputThreadProcWin32(LPVOID /*lpParameter*/) // successfully created with the owner configured when the window is // first created. See GH#13066 for details. ServiceLocator::LocateGlobals().getConsoleInformation().GetVtIo()->CreatePseudoWindow(); + + // Register the pseudoconsole window as being owned by the root process. + const auto pseudoWindow = ServiceLocator::LocatePseudoWindow(); + SetConsoleWindowOwner(pseudoWindow, nullptr); } UnlockConsole();