diff --git a/src/interactivity/base/InteractivityFactory.cpp b/src/interactivity/base/InteractivityFactory.cpp index 3fd2b636856..31fbb4b9f53 100644 --- a/src/interactivity/base/InteractivityFactory.cpp +++ b/src/interactivity/base/InteractivityFactory.cpp @@ -352,7 +352,7 @@ using namespace Microsoft::Console::Interactivity; const auto gle = GetLastError(); status = NTSTATUS_FROM_WIN32(gle); } - + _pseudoConsoleWindowHwnd = hwnd; break; } #ifdef BUILD_ONECORE_INTERACTIVITY @@ -464,6 +464,18 @@ using namespace Microsoft::Console::Interactivity; _WritePseudoWindowCallback((bool)wParam); } } + case WM_GETOBJECT: + { + if (static_cast(lParam) == static_cast(UiaRootObjectId)) + { + if (nullptr == _pPseudoConsoleUiaProvider) + { + LOG_IF_FAILED(WRL::MakeAndInitialize(&_pPseudoConsoleUiaProvider, _pseudoConsoleWindowHwnd)); + } + return UiaReturnRawElementProvider(hWnd, wParam, lParam, _pPseudoConsoleUiaProvider.Get()); + } + return 0; + } } // If we get this far, call the default window proc return DefWindowProcW(hWnd, Message, wParam, lParam); diff --git a/src/interactivity/base/InteractivityFactory.hpp b/src/interactivity/base/InteractivityFactory.hpp index 5497841749b..2a0b3460eef 100644 --- a/src/interactivity/base/InteractivityFactory.hpp +++ b/src/interactivity/base/InteractivityFactory.hpp @@ -8,6 +8,7 @@ #include "ApiDetector.hpp" #include "../inc/IInteractivityFactory.hpp" +#include "PseudoConsoleWindowAccessibilityProvider.hpp" #include @@ -40,5 +41,8 @@ namespace Microsoft::Console::Interactivity private: void _WritePseudoWindowCallback(bool showOrHide); + + HWND _pseudoConsoleWindowHwnd{ nullptr }; + WRL::ComPtr _pPseudoConsoleUiaProvider{ nullptr }; }; } diff --git a/src/interactivity/base/PseudoConsoleWindowAccessibilityProvider.cpp b/src/interactivity/base/PseudoConsoleWindowAccessibilityProvider.cpp new file mode 100644 index 00000000000..09d7507639b --- /dev/null +++ b/src/interactivity/base/PseudoConsoleWindowAccessibilityProvider.cpp @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "PseudoConsoleWindowAccessibilityProvider.hpp" + +using namespace Microsoft::Console::Interactivity; + +HRESULT PseudoConsoleWindowAccessibilityProvider::RuntimeClassInitialize(HWND pseudoConsoleHwnd) noexcept +{ + RETURN_HR_IF_NULL(E_INVALIDARG, pseudoConsoleHwnd); + _pseudoConsoleHwnd = pseudoConsoleHwnd; + return S_OK; +} + +IFACEMETHODIMP PseudoConsoleWindowAccessibilityProvider::get_ProviderOptions(_Out_ ProviderOptions* pOptions) +{ + RETURN_HR_IF_NULL(E_INVALIDARG, pOptions); + *pOptions = ProviderOptions_ServerSideProvider; + return S_OK; +} + +IFACEMETHODIMP PseudoConsoleWindowAccessibilityProvider::GetPatternProvider(_In_ PATTERNID /*iid*/, + _COM_Outptr_result_maybenull_ IUnknown** ppInterface) +{ + RETURN_HR_IF_NULL(E_INVALIDARG, ppInterface); + *ppInterface = nullptr; + return S_OK; +} + +IFACEMETHODIMP PseudoConsoleWindowAccessibilityProvider::GetPropertyValue(_In_ PROPERTYID propertyId, + _Out_ VARIANT* pVariant) +{ + RETURN_HR_IF_NULL(E_INVALIDARG, pVariant); + + pVariant->vt = VT_EMPTY; + + // Returning the default will leave the property as the default + // so we only really need to touch it for the properties we want to implement + switch (propertyId) + { + case UIA_ControlTypePropertyId: + { + pVariant->vt = VT_I4; + pVariant->lVal = UIA_WindowControlTypeId; + break; + } + case UIA_NamePropertyId: + { + static constexpr auto AutomationPropertyName = L"Internal Console Management Window"; + pVariant->bstrVal = SysAllocString(AutomationPropertyName); + if (pVariant->bstrVal != nullptr) + { + pVariant->vt = VT_BSTR; + } + break; + } + case UIA_IsControlElementPropertyId: + case UIA_IsContentElementPropertyId: + case UIA_IsKeyboardFocusablePropertyId: + case UIA_HasKeyboardFocusPropertyId: + { + pVariant->vt = VT_BOOL; + pVariant->boolVal = VARIANT_FALSE; + break; + } + default: + break; + } + return S_OK; +} + +IFACEMETHODIMP PseudoConsoleWindowAccessibilityProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) +{ + RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider); + RETURN_HR_IF_NULL(gsl::narrow_cast(UIA_E_ELEMENTNOTAVAILABLE), _pseudoConsoleHwnd); + return UiaHostProviderFromHwnd(_pseudoConsoleHwnd, ppProvider); +} diff --git a/src/interactivity/base/PseudoConsoleWindowAccessibilityProvider.hpp b/src/interactivity/base/PseudoConsoleWindowAccessibilityProvider.hpp new file mode 100644 index 00000000000..ab30eeeec44 --- /dev/null +++ b/src/interactivity/base/PseudoConsoleWindowAccessibilityProvider.hpp @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "precomp.h" + +namespace Microsoft::Console::Interactivity +{ + class PseudoConsoleWindowAccessibilityProvider final : + public WRL::RuntimeClass, IRawElementProviderSimple> + { + public: + PseudoConsoleWindowAccessibilityProvider() = default; + ~PseudoConsoleWindowAccessibilityProvider() = default; + HRESULT RuntimeClassInitialize(HWND pseudoConsoleHwnd) noexcept; + + PseudoConsoleWindowAccessibilityProvider(const PseudoConsoleWindowAccessibilityProvider&) = delete; + PseudoConsoleWindowAccessibilityProvider(PseudoConsoleWindowAccessibilityProvider&&) = delete; + PseudoConsoleWindowAccessibilityProvider& operator=(const PseudoConsoleWindowAccessibilityProvider&) = delete; + PseudoConsoleWindowAccessibilityProvider& operator=(PseudoConsoleWindowAccessibilityProvider&&) = delete; + + // IRawElementProviderSimple methods + IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions) override; + IFACEMETHODIMP GetPatternProvider(_In_ PATTERNID iid, + _COM_Outptr_result_maybenull_ IUnknown** ppInterface) override; + IFACEMETHODIMP GetPropertyValue(_In_ PROPERTYID idProp, + _Out_ VARIANT* pVariant) override; + IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) override; + + private: + HWND _pseudoConsoleHwnd; + }; +} diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj b/src/interactivity/base/lib/InteractivityBase.vcxproj index 0b9a03e97a3..7517d99f4f6 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj @@ -28,6 +28,7 @@ Create + @@ -49,9 +50,10 @@ + - + \ No newline at end of file diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters index d761db55b44..3cad47ac50c 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters @@ -36,6 +36,12 @@ Source Files + + Source Files + + + Source Files + @@ -89,6 +95,9 @@ Header Files + + Header Files + diff --git a/src/interactivity/base/sources.inc b/src/interactivity/base/sources.inc index 5d77a022b0b..0e77324d005 100644 --- a/src/interactivity/base/sources.inc +++ b/src/interactivity/base/sources.inc @@ -42,10 +42,11 @@ SOURCES = \ ..\VtApiRedirection.cpp \ ..\EventSynthesis.cpp \ ..\RemoteConsoleControl.cpp \ - ..\HostSignalInputThread.cpp \ + ..\HostSignalInputThread.cpp \ + ..\PseudoConsoleWindowAccessibilityProvider.cpp \ INCLUDES = \ $(INCLUDES); \ ..; \ ..\..\..\..\..\ConIoSrv; \ - +