Skip to content

Commit bf6ff0d

Browse files
Merge 862d8a3 into 21a61e5
2 parents 21a61e5 + 862d8a3 commit bf6ff0d

15 files changed

Lines changed: 547 additions & 4 deletions

File tree

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,9 @@
3030
[submodule "include/Detours"]
3131
path = include/detours
3232
url = https://github.com/microsoft/Detours/
33+
[submodule "include/microsoft-ui-uiautomation"]
34+
path = include/microsoft-ui-uiautomation
35+
url = https://github.com/michaeldcurran/microsoft-ui-uiautomation
36+
[submodule "include/wil"]
37+
path = include/wil
38+
url = https://github.com/microsoft/wil

include/microsoft-ui-uiautomation

include/wil

Submodule wil added at 3234003

nvdaHelper/UIARemote/UIARemote.cpp

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+

nvdaHelper/UIARemote/remoteLog.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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+
#pragma once
16+
17+
// Converts a utf8 encoded string into a utf16 encoded wstring
18+
std::wstring stringToWstring(const std::string& from) {
19+
int wideLen = MultiByteToWideChar(CP_UTF8, 0, from.c_str(), from.length(), nullptr, 0);
20+
std::wstring wideBuf (wideLen, L'\0');
21+
MultiByteToWideChar(CP_UTF8, 0, from.c_str(), from.length(), wideBuf.data(), wideLen);
22+
return wideBuf;
23+
}
24+
25+
const std::wstring endl{L"\n"};
26+
27+
class RemoteableLogger;
28+
29+
// A class for logging messages from within a remote ops call.
30+
// Push messages to the object with << just like an ostream.
31+
// Currently standard strings, UiaStrings, and UiaInt instances are supported.
32+
// After remote execution is complete, call dumpLog to write the content to our standard logging framework.
33+
class RemoteableLogger {
34+
public:
35+
36+
RemoteableLogger(UiaOperationScope& scope): _log{} {
37+
scope.BindResult(_log);
38+
}
39+
40+
RemoteableLogger& operator <<(UiaInt& message) {
41+
_log.Append(message.Stringify());
42+
return *this;
43+
}
44+
45+
RemoteableLogger& operator <<(UiaString& message) {
46+
_log.Append(message);
47+
return *this;
48+
}
49+
50+
RemoteableLogger& operator <<(const std::wstring message) {
51+
_log.Append(message);
52+
return *this;
53+
}
54+
55+
void dumpLog() {
56+
assert(!UiaOperationAbstraction::ShouldUseRemoteApi());
57+
std::wstring messageBlock{L"Dump log start:\n"};
58+
try {
59+
// locally, a UiaArray is a shared_ptr to a vector of will_shared_bstr
60+
const std::vector<wil::shared_bstr>& v = *_log;
61+
for(const auto& message: v) {
62+
messageBlock+=message.get();
63+
}
64+
} catch (std::exception& e) {
65+
auto wideWhat = stringToWstring(e.what());
66+
messageBlock += L"dumpLog exception: ";
67+
messageBlock += wideWhat;
68+
messageBlock += L"\n";
69+
}
70+
messageBlock+=L"Dump log end";
71+
LOG_DEBUG(messageBlock);
72+
}
73+
74+
private:
75+
UiaArray<UiaString> _log;
76+
77+
};

nvdaHelper/UIARemote/sconscript

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
###
2+
#This file is a part of the NVDA project.
3+
#URL: http://www.nvda-project.org/
4+
#Copyright 2021 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+
Import([
16+
'env',
17+
'localLib',
18+
'MSUIA_lib_outDir',
19+
'MSUIA_include_outDir',
20+
])
21+
22+
env = env.Clone()
23+
env.Append(CPPPATH=Dir('#include/wil/include'))
24+
env.Append(CPPPATH=MSUIA_include_outDir)
25+
env.Append(CCFLAGS='/MD')
26+
27+
UIARemoteLib=env.SharedLibrary(
28+
target="UIARemote",
29+
source=[
30+
env['projectResFile'],
31+
"UIARemote.cpp",
32+
],
33+
LIBS=[
34+
"runtimeobject",
35+
"UIAutomationCore",
36+
localLib[2],
37+
MSUIA_lib_outDir.File('UiaOperationAbstraction.lib'),
38+
],
39+
)
40+
41+
Return('UIARemoteLib')

nvdaHelper/archBuild_sconscript

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,13 @@ if TARGET_ARCH=='x86':
209209
if signExec:
210210
env.AddPostAction(win10localLib[0],[signExec])
211211
env.Install(libInstallDir,win10localLib)
212+
MSUIA_lib_outDir,MSUIA_include_outDir = thirdPartyEnv.SConscript('microsoft-ui-uiautomation/sconscript')
213+
Export('MSUIA_lib_outDir')
214+
Export('MSUIA_include_outDir')
215+
UIARemoteLib=env.SConscript('UIARemote/sconscript')
216+
if signExec:
217+
env.AddPostAction(UIARemoteLib[0],[signExec])
218+
env.Install(libInstallDir,UIARemoteLib)
212219

213220
clientLib=env.SConscript('client/sconscript')
214221
Export('clientLib')
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
3+
<assemblyIdentity version="1.0.0.0" name="UIARemote"/>
4+
5+
<file name="Microsoft.UI.UIAutomation.dll">
6+
<activatableClass
7+
name="Microsoft.UI.UIAutomation.AutomationRemoteOperation"
8+
threadingModel="both"
9+
xmlns="urn:schemas-microsoft-com:winrt.v1" />
10+
</file>
11+
12+
</assembly>

0 commit comments

Comments
 (0)