|
| 1 | +/* |
| 2 | +This file is a part of the NVDA project. |
| 3 | +URL: http://www.nvaccess.org/ |
| 4 | +Copyright 2021-2022 NV Access Limited |
| 5 | + This program is free software: you can redistribute it and/or modify |
| 6 | + it under the terms of the GNU General Public License version 2.0, as published by |
| 7 | + the Free Software Foundation. |
| 8 | + This program is distributed in the hope that it will be useful, |
| 9 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 11 | +This license can be found at: |
| 12 | +http://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
| 13 | +*/ |
| 14 | + |
| 15 | +#include <memory> |
| 16 | +#include <functional> |
| 17 | +#include <string> |
| 18 | +#include <windows.h> |
| 19 | +#include <atlsafe.h> |
| 20 | +#include <atlcomcli.h> |
| 21 | +#include <roapi.h> |
| 22 | +#include <winstring.h> |
| 23 | +#include <UIAutomation.h> |
| 24 | +#include <UiaOperationAbstraction/UiaOperationAbstraction.h> |
| 25 | +#include <UiaOperationAbstraction/SafeArrayUtil.h> |
| 26 | +#include <common/log.h> |
| 27 | +#include <winrt/microsoft.ui.uiautomation.h> |
| 28 | + |
| 29 | +using namespace UiaOperationAbstraction; |
| 30 | + |
| 31 | +#include "remoteLog.h" |
| 32 | + |
| 33 | +wchar_t dllDirectory[MAX_PATH]; |
| 34 | + |
| 35 | +// Several custom extension GUIDs specific to Microsoft Word |
| 36 | +winrt::guid guid_msWord_extendedTextRangePattern{ 0x93514122, 0xff04, 0x4b2c, { 0xa4, 0xad, 0x4a, 0xb0, 0x45, 0x87, 0xc1, 0x29 } }; |
| 37 | +winrt::guid guid_msWord_getCustomAttributeValue{ 0x81aca91, 0x32f2, 0x46f0, { 0x9f, 0xb9, 0x1, 0x70, 0x38, 0xbc, 0x45, 0xf8 } }; |
| 38 | + |
| 39 | +bool _isInitialized {false}; |
| 40 | + |
| 41 | +// Fetches a custom attribute value from a range of text in Microsoft Word. |
| 42 | +// this function uses the UI automation Operation Abstraction API to call the Microsoft Word specific custom extension |
| 43 | +extern "C" __declspec(dllexport) bool __stdcall msWord_getCustomAttributeValue(IUIAutomationElement* docElementArg, IUIAutomationTextRange* pTextRangeArg, int customAttribIDArg, VARIANT* pCustomAttribValueArg) { |
| 44 | + if(!_isInitialized) { |
| 45 | + LOG_ERROR(L"UIARemote not initialized!"); |
| 46 | + return false; |
| 47 | + } |
| 48 | + try { |
| 49 | + auto scope=UiaOperationScope::StartNew(); |
| 50 | + RemoteableLogger logger{scope}; |
| 51 | + // Here starts declaritive code which will be executed remotely |
| 52 | + logger<<L"Remoting msWord_getCustomAttributeValue"<<endl; |
| 53 | + UiaBool isExtensionSupported{false}; |
| 54 | + UiaElement docElement{docElementArg}; |
| 55 | + UiaTextRange textRange{pTextRangeArg}; |
| 56 | + UiaInt customAttribID{customAttribIDArg}; |
| 57 | + UiaVariant customAttribValue; |
| 58 | + scope.If( |
| 59 | + /* condition */ docElement.IsExtensionSupported(guid_msWord_extendedTextRangePattern), |
| 60 | + /* body */ [&]() { |
| 61 | + logger<<L"guid_msWord_extendedTextRangePattern is supported extension"<<endl; |
| 62 | + UiaElement patternElement{nullptr}; |
| 63 | + docElement.CallExtension(guid_msWord_extendedTextRangePattern, patternElement); |
| 64 | + scope.If( |
| 65 | + /* condition */ patternElement, |
| 66 | + /* body */ [&]() { |
| 67 | + logger<<L"Got custom pattern element "<<endl; |
| 68 | + scope.If( |
| 69 | + /* condition */ patternElement.IsExtensionSupported(guid_msWord_getCustomAttributeValue), |
| 70 | + /* body */ [&]() { |
| 71 | + isExtensionSupported = true; |
| 72 | + logger<<L"guid_msWord_getCustomAttributeValue extension supported on pattern"<<endl; |
| 73 | + patternElement.CallExtension(guid_msWord_getCustomAttributeValue, textRange, customAttribID, customAttribValue); |
| 74 | + logger<<L"Called guid_msWord_getCustomAttributeValue extention"<<endl; |
| 75 | + }, |
| 76 | + /* else */ [&]() { |
| 77 | + logger<<L"No guid_msWord_getCustomAttributeValue extension supported"<<endl; |
| 78 | + } |
| 79 | + ); |
| 80 | + }, |
| 81 | + /* else */ [&]() { |
| 82 | + logger<<L"Could not fetch guid_msWord_extendedTextRangePattern pattern"<<endl; |
| 83 | + } |
| 84 | + ); |
| 85 | + }, |
| 86 | + /* else */ [&]() { |
| 87 | + logger<<L"No guid_msWord_extendedTextRangePattern extension supported"<<endl; |
| 88 | + } |
| 89 | + ); |
| 90 | + // Request that certain variables be made available locally after execution remotely |
| 91 | + scope.BindResult(isExtensionSupported, customAttribValue); |
| 92 | + // Actually execute the remote code |
| 93 | + auto res = scope.ResolveHr(); |
| 94 | + if(res != S_OK) { |
| 95 | + LOG_ERROR(L"Error in scope.Resolve: code "<<res); |
| 96 | + return false; |
| 97 | + } |
| 98 | + logger.dumpLog(); |
| 99 | + // We are back to local again |
| 100 | + if(isExtensionSupported) { |
| 101 | + if(customAttribValue.IsInt()) { |
| 102 | + pCustomAttribValueArg->vt = VT_I4; |
| 103 | + pCustomAttribValueArg->lVal = customAttribValue.AsInt(); |
| 104 | + return true; |
| 105 | + } else if(customAttribValue.IsString()) { |
| 106 | + pCustomAttribValueArg->vt = VT_BSTR; |
| 107 | + pCustomAttribValueArg->bstrVal = customAttribValue.AsString().get(); |
| 108 | + return true; |
| 109 | + } else { |
| 110 | + LOG_ERROR(L"Unknown data type"); |
| 111 | + return false; |
| 112 | + } |
| 113 | + } else { |
| 114 | + LOG_DEBUG(L"Extension not supported"); |
| 115 | + } |
| 116 | + } catch (std::exception& e) { |
| 117 | + auto wideWhat = stringToWstring(e.what()); |
| 118 | + LOG_ERROR(L"msWord_getCustomAttributeValue exception: "<<wideWhat); |
| 119 | + } catch(...) { |
| 120 | + LOG_ERROR(L"msWord_getCustomAttributeValue exception: unknown"); |
| 121 | + } |
| 122 | + return false; |
| 123 | +} |
| 124 | + |
| 125 | +// Registers and initializes the Microsoft-ui-UIAutomation remote operations library. |
| 126 | +extern "C" __declspec(dllexport) bool __stdcall initialize(bool doRemote, IUIAutomation* client) { |
| 127 | + std::wstring manifestPath = dllDirectory; |
| 128 | + manifestPath += L"\\Microsoft.UI.UIAutomation.dll.manifest"; |
| 129 | + ACTCTX actCtx{}; |
| 130 | + actCtx.cbSize=sizeof(actCtx); |
| 131 | + actCtx.lpSource = L"Microsoft.UI.UIAutomation.dll.manifest"; |
| 132 | + actCtx.lpAssemblyDirectory = dllDirectory; |
| 133 | + actCtx.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID; |
| 134 | + HANDLE hActCtx=CreateActCtx(&actCtx); |
| 135 | + if(hActCtx == nullptr) { |
| 136 | + LOG_ERROR(L"Could not create activation context for "<<manifestPath); |
| 137 | + return false; |
| 138 | + } |
| 139 | + ULONG_PTR actCtxCookie; |
| 140 | + if(!ActivateActCtx(hActCtx,&actCtxCookie)) { |
| 141 | + LOG_ERROR(L"Error activating activation context for "<<manifestPath); |
| 142 | + ReleaseActCtx(hActCtx); |
| 143 | + return false; |
| 144 | + } |
| 145 | + LOG_INFO(L"Registered "<<manifestPath); |
| 146 | + if(!winrt::get_activation_factory<winrt::Microsoft::UI::UIAutomation::AutomationRemoteOperation>()) { |
| 147 | + LOG_ERROR(L"Unable to get Microsoft.UI.UIAutomation activation factory"); |
| 148 | + return false; |
| 149 | + } |
| 150 | + LOG_INFO(L"Microsoft.UI.UIAutomation is available"); |
| 151 | + UiaOperationAbstraction::Initialize(doRemote,client); |
| 152 | + _isInitialized = true; |
| 153 | + return true; |
| 154 | +} |
| 155 | + |
| 156 | +BOOL WINAPI DllMain(HINSTANCE hModule,DWORD reason,LPVOID lpReserved) { |
| 157 | + if(reason==DLL_PROCESS_ATTACH) { |
| 158 | + GetModuleFileName(hModule,dllDirectory,MAX_PATH); |
| 159 | + PathRemoveFileSpec(dllDirectory); |
| 160 | + } |
| 161 | + return true; |
| 162 | +} |
| 163 | + |
0 commit comments