Skip to content

Commit

Permalink
Merged win8Injection branch. This means that installed releases of NV…
Browse files Browse the repository at this point in the history
…DA can now hook into metro style apps, enabling features such as speaking typed characters, browse mode for web documents (including full support for metro version of Internet Explorer 10), and displayModel (if GDI is ever used). Please note that this will *only* work if NVDA runs with the uiAccess privilidge, which for now only is installed official releases (not snapshots or any portable copies).

A lot of NVDA's rpc and injection code needed to be rewritten to handle metro style apps. Therefore please watch out for and report any weirdness resulting from this revision (especially on Operating Systems before Windows 8).
  • Loading branch information
michaelDCurran committed Apr 20, 2012
2 parents 893621b + aaa57e8 commit 54b96a9
Show file tree
Hide file tree
Showing 34 changed files with 301 additions and 157 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
implicit_handle(handle_t nvdaControllerInternalBindingHandle)
]
interface NvdaControllerInternal {
[fault_status,comm_status] getNVDAProcessID();
[fault_status,comm_status] requestRegistration();
[fault_status,comm_status] inputLangChangeNotify();
[fault_status,comm_status] typedCharacterNotify();
[fault_status,comm_status] displayModelTextChangeNotify();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@ cpp_quote("*/")
]
interface NvdaControllerInternal {

/**
* Gets the process ID of NVDA.
* @param pProcessID memory where the retreaved process ID can be placed.
*/
error_status_t __stdcall getNVDAProcessID([out] long* pProcessID);
error_status_t __stdcall requestRegistration([in,string] const wchar_t* uuidString);

/**
* Notifies NVDA that the keyboard layout has changed for this thread.
Expand Down
3 changes: 3 additions & 0 deletions nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.acf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ This license can be found at:
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/

[strict_context_handle]
interface NvdaInProcUtils {
[fault_status,comm_status] registerNVDAProcess();
[fault_status,comm_status] unregisterNVDAProcess();
[fault_status,comm_status] winword_expandToLine();
[fault_status,comm_status] winword_getTextInRange();
}
5 changes: 5 additions & 0 deletions nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.idl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ import "oaidl.idl";
]
interface NvdaInProcUtils {

typedef [context_handle] void* nvdaRegistrationHandle_t;

error_status_t registerNVDAProcess([in] handle_t bindingHandle, [out] nvdaRegistrationHandle_t* registrationhandle);
error_status_t unregisterNVDAProcess([in,out] nvdaRegistrationHandle_t* registrationhandle);

error_status_t winword_expandToLine([in] const long windowHandle, [in] const int offset, [out] int* lineStart, [out] int* lineEnd);

error_status_t winword_getTextInRange([in] const long windowHandle, [in] const int startOffset, [in] const int endOffset, [in] const long formatConfig, [out] BSTR* text);
Expand Down
6 changes: 3 additions & 3 deletions nvdaHelper/local/nvdaControllerInternal.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ This license can be found at:

#include "nvdaControllerInternal.h"

error_status_t __stdcall nvdaControllerInternal_getNVDAProcessID(long* pProcessID) {
*pProcessID=GetCurrentProcessId();
return RPC_S_OK;
error_status_t(__stdcall *_nvdaControllerInternal_requestRegistration)(const wchar_t*);
error_status_t __stdcall nvdaControllerInternal_requestRegistration(const wchar_t* uuidString) {
return _nvdaControllerInternal_requestRegistration(uuidString);
}

error_status_t(__stdcall *_nvdaControllerInternal_inputLangChangeNotify)(const long, const unsigned long, const wchar_t*);
Expand Down
56 changes: 47 additions & 9 deletions nvdaHelper/local/nvdaHelperLocal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,63 @@ This license can be found at:
#include <cstdio>
#include <sstream>
#include <algorithm>
#include <rpc.h>
#include <sddl.h>
#include <common/log.h>
#include "nvdaControllerInternal.h"
#include "nvdaHelperLocal.h"
#include "dllImportTableHooks.h"
#include "rpcsrv.h"

DllImportTableHooks* oleaccHooks = NULL;
DllImportTableHooks* uiaCoreHooks = NULL;

handle_t createConnection(int processID) {
typedef struct _RPC_SECURITY_QOS_V5_W {
unsigned long Version;
unsigned long Capabilities;
unsigned long IdentityTracking;
unsigned long ImpersonationType;
unsigned long AdditionalSecurityInfoType;
union
{
RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials;
} u;
void *Sid;
unsigned int EffectiveOnly;
void *ServerSecurityDescriptor;
} RPC_SECURITY_QOS_V5_W, *PRPC_SECURITY_QOS_V5_W;

handle_t createRemoteBindingHandle(wchar_t* uuidString) {
RPC_STATUS rpcStatus;
std::wostringstream addr;
addr<<L"ncalrpc:[nvdaHelperRemote_"<<processID<<L"]";
RPC_WSTR stringBinding;
if((rpcStatus=RpcStringBindingCompose((RPC_WSTR)uuidString,(RPC_WSTR)L"ncalrpc",NULL,NULL,NULL,&stringBinding))!=RPC_S_OK) {
LOG_ERROR(L"RpcStringBindingCompose failed with status "<<rpcStatus);
return NULL;
}
handle_t bindingHandle;
if((rpcStatus=RpcBindingFromStringBinding((RPC_WSTR)(addr.str().c_str()),&bindingHandle))!=RPC_S_OK) {
fprintf(stderr,"Error creating binding handle from string binding, rpc code 0X%X\n",rpcStatus);
if((rpcStatus=RpcBindingFromStringBinding(stringBinding,&bindingHandle))!=RPC_S_OK) {
LOG_ERROR(L"RpcBindingFromStringBinding failed with status "<<rpcStatus);
return NULL;
}
//On Windows 8 we must allow AppContainer servers to communicate back to us
//Detect Windows 8 by looking for RpcServerRegisterIf3
HANDLE rpcrt4Handle=GetModuleHandle(L"rpcrt4.dll");
if(rpcrt4Handle&&GetProcAddress((HMODULE)rpcrt4Handle,"RpcServerRegisterIf3")) {
PSECURITY_DESCRIPTOR psd=NULL;
ULONG size;
if(!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:(A;;GA;;;wd)(A;;GA;;;AC)",SDDL_REVISION_1,&psd,&size)) {
LOG_ERROR(L"ConvertStringSecurityDescriptorToSecurityDescriptor failed");
return NULL;
}
RPC_SECURITY_QOS_V5_W securityQos={5,0,0,0,0,NULL,NULL,0,psd};
if((rpcStatus=RpcBindingSetAuthInfoEx(bindingHandle,NULL,RPC_C_AUTHN_LEVEL_DEFAULT,RPC_C_AUTHN_DEFAULT,NULL,0,(RPC_SECURITY_QOS*)&securityQos))!=RPC_S_OK) {
LOG_ERROR(L"RpcBindingSetAuthInfoEx failed with status "<<rpcStatus);
return NULL;
}
}
return bindingHandle;
}

void destroyConnection(handle_t bindingHandle) {
RpcBindingFree(&bindingHandle);
}

bool shouldCancelSendMessage;
const UINT CANCELSENDMESSAGE_CHECK_INTERVAL = 400;

Expand Down Expand Up @@ -113,3 +147,7 @@ void nvdaHelperLocal_terminate() {
}
stopServer();
}

void logMessage(int level, const wchar_t* msg) {
nvdaControllerInternal_logMessage(level,0,msg);
}
6 changes: 4 additions & 2 deletions nvdaHelper/local/nvdaHelperLocal.def
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
EXPORTS
createConnection
destroyConnection
createRemoteBindingHandle
cancellableSendMessageTimeout
cancelSendMessage
_notifySendMessageCancelled
nvdaHelperLocal_initialize
nvdaHelperLocal_terminate
generateBeep
nvdaInProcUtils_registerNVDAProcess
nvdaInProcUtils_unregisterNVDAProcess
nvdaInProcUtils_winword_expandToLine
nvdaInProcUtils_winword_getTextInRange
VBuf_createBuffer
Expand All @@ -25,6 +26,7 @@ EXPORTS
VBuf_locateControlFieldNodeAtOffset
VBuf_locateTextFieldNodeAtOffset
VBuf_setSelectionOffsets
_nvdaControllerInternal_requestRegistration
_nvdaControllerInternal_displayModelTextChangeNotify
_nvdaControllerInternal_inputLangChangeNotify
_nvdaControllerInternal_logMessage
Expand Down
3 changes: 1 addition & 2 deletions nvdaHelper/local/nvdaHelperLocal.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ This license can be found at:
#define NVDAHELPERLOCAL_H
#include <rpc.h>

handle_t createConnection(int processID);
void destroyConnection(handle_t bindingHandle);
handle_t createRemoteBindingHandle(wchar_t* uuidString);
LRESULT cancellableSendMessageTimeout(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult);
void cancelSendMessage();
void nvdaHelperLocal_initialize();
Expand Down
48 changes: 35 additions & 13 deletions nvdaHelper/local/rpcSrv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ This license can be found at:
#include <cstdio>
#include <sstream>
#include <rpc.h>
#include <sddl.h>
#include "nvdaController.h"
#include "nvdaControllerInternal.h"
#include <common/winIPCUtils.h>
#include <common/log.h>
#include "rpcSrv.h"

using namespace std;

typedef RPC_STATUS(RPC_ENTRY *RpcServerRegisterIf3_functype)(RPC_IF_HANDLE,UUID __RPC_FAR*,RPC_MGR_EPV __RPC_FAR*,unsigned int,unsigned int,unsigned int,RPC_IF_CALLBACK_FN __RPC_FAR*,void __RPC_FAR*);

RPC_IF_HANDLE availableInterfaces[]={
nvdaController_NvdaController_v1_0_s_ifspec,
nvdaControllerInternal_NvdaControllerInternal_v1_0_s_ifspec
Expand All @@ -40,36 +44,54 @@ void __RPC_USER midl_user_free(void* p) {

RPC_STATUS startServer() {
RPC_STATUS status;
//Set the protocol
wchar_t desktopSpecificNamespace[64];
generateDesktopSpecificNamespace(desktopSpecificNamespace,ARRAYSIZE(desktopSpecificNamespace));
wstringstream endpointStringStream;
endpointStringStream<<L"NvdaCtlr."<<desktopSpecificNamespace;
status=RpcServerUseProtseqEp((RPC_WSTR)L"ncalrpc",RPC_C_PROTSEQ_MAX_REQS_DEFAULT,(RPC_WSTR)(endpointStringStream.str().c_str()),NULL);
wstring endpointString=L"NvdaCtlr.";
endpointString+=desktopSpecificNamespace;
//On Windows 8 the new rpcServerRegisterIf3 must be used along with a security descriptor allowing appContainer access
HANDLE rpcrt4Handle=GetModuleHandle(L"rpcrt4.dll");
RpcServerRegisterIf3_functype RpcServerRegisterIf3=(RpcServerRegisterIf3_functype)GetProcAddress((HMODULE)rpcrt4Handle,"RpcServerRegisterIf3");
PSECURITY_DESCRIPTOR psd=NULL;
ULONG size;
if(RpcServerRegisterIf3) {
if(!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:(A;;GA;;;wd)(A;;GA;;;AC)",SDDL_REVISION_1,&psd,&size)||!psd) {
LOG_ERROR(L"ConvertStringSecurityDescriptorToSecurityDescriptor failed, GetLastError is "<<GetLastError());
return -1;
}
}
status=RpcServerUseProtseqEp((RPC_WSTR)L"ncalrpc",RPC_C_PROTSEQ_MAX_REQS_DEFAULT,(RPC_WSTR)(endpointString.c_str()),psd);
//We can ignore the error where the endpoint is already set
if(status!=RPC_S_OK&&status!=RPC_S_DUPLICATE_ENDPOINT) {
LOG_ERROR(L"RpcUseProtSeqEp failed with status "<<status);
return status;
}
//Register the interfaces
for(int i=0;i<ARRAYSIZE(availableInterfaces);i++) {
if((status=RpcServerRegisterIf(availableInterfaces[i],NULL,NULL))!=RPC_S_OK) {
return status;
if(RpcServerRegisterIf3) { //Windows 8
for(int i=0;i<ARRAYSIZE(availableInterfaces);i++) {
if((status=RpcServerRegisterIf3(availableInterfaces[i],NULL,NULL,RPC_IF_AUTOLISTEN|RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,RPC_C_LISTEN_MAX_CALLS_DEFAULT,0,NULL,psd))!=RPC_S_OK) {
LOG_ERROR(L"RpcServerRegisterIf3 failed to register interface at index "<<i<<L", status "<<status);
return status;
}
}
} else { // Pre Windows 8
for(int i=0;i<ARRAYSIZE(availableInterfaces);i++) {
if((status=RpcServerRegisterIfEx(availableInterfaces[i],NULL,NULL,RPC_IF_AUTOLISTEN,RPC_C_LISTEN_MAX_CALLS_DEFAULT,NULL))!=RPC_S_OK) {
LOG_ERROR(L"RpcServerRegisterIf failed to register interface at index "<<i<<L", status "<<status);
return status;
}
}
}
//Start listening
if((status=RpcServerListen(1,RPC_C_LISTEN_MAX_CALLS_DEFAULT,TRUE))!=RPC_S_OK) {
return status;
}
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RpcMgmtWaitServerListen,NULL,0,NULL);
LocalFree(psd);
return status;
}

RPC_STATUS stopServer() {
RPC_STATUS status;
for(int i=0;i<ARRAYSIZE(availableInterfaces);i++) {
if((status=RpcServerUnregisterIf(availableInterfaces[i],NULL,1))!=RPC_S_OK) {
LOG_ERROR(L"RpcServerUnregisterIf failed to unregister interface at index "<<i<<L", status "<<status);
return status;
}
}
return RpcMgmtStopServerListening(NULL);
return status;
}
1 change: 1 addition & 0 deletions nvdaHelper/local/sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ localLib=env.SharedLibrary(
"textUtils.cpp",
],
LIBS=[
"advapi32.lib",
"user32",
"usp10",
"ole32",
Expand Down
2 changes: 1 addition & 1 deletion nvdaHelper/remote/IA2Support.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This license can be found at:
#include <objbase.h>
#include <ia2.h>
#include "nvdaControllerInternal.h"
#include "log.h"
#include <common/log.h>
#include "nvdaHelperRemote.h"
#include "dllmain.h"
#include "IA2Support.h"
Expand Down
15 changes: 14 additions & 1 deletion nvdaHelper/remote/apiHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ This license can be found at:
#include <set>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <delayimp.h>
#include <minHook/newMinHook.h>
#include "nvdaControllerInternal.h"
#include "log.h"
#include <common/log.h>
#include "dllmain.h"
#include "apiHook.h"

using namespace std;
Expand All @@ -28,10 +30,17 @@ typedef set<void*> functionSet_t;

moduleSet_t g_hookedModules;
functionSet_t g_hookedFunctions;
HMODULE minhookLibHandle=NULL;

bool apiHook_initialize() {
LOG_DEBUG("calling MH_Initialize");
int res;
wstring dllPath=dllDirectory;
dllPath+=L"\\minhook.dll";
if((minhookLibHandle=LoadLibrary(dllPath.c_str()))==NULL) {
LOG_ERROR(L"LoadLibrary failed to load "<<dllPath);
return false;
}
if ((res=MH_Initialize())!=MH_OK) {
LOG_ERROR("MH_CreateHook failed with " << res);
return false;
Expand Down Expand Up @@ -85,5 +94,9 @@ bool apiHook_terminate() {
FreeLibrary(*i);
}
g_hookedModules.clear();
if(minhookLibHandle) FreeLibrary(minhookLibHandle);
if(!__FUnloadDelayLoadedDLL2("minHook.DLL")) {
LOG_ERROR(L"__FUnloadDelayLoadedDLL2 failed to unload minhook.dll");
}
return TRUE;
}
2 changes: 1 addition & 1 deletion nvdaHelper/remote/displayModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This license can be found at:
#include <set>
#include <common/xml.h>
#include "nvdaControllerInternal.h"
#include "log.h"
#include <common/log.h>
#include "displayModel.h"

using namespace std;
Expand Down
2 changes: 1 addition & 1 deletion nvdaHelper/remote/gdiHooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This license can be found at:
#include "dllmain.h"
#include "apiHook.h"
#include "displayModel.h"
#include "log.h"
#include <common/log.h>
#include "nvdaControllerInternal.h"
#include <common/lock.h>
#include "gdiHooks.h"
Expand Down
5 changes: 1 addition & 4 deletions nvdaHelper/remote/inProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ This license can be found at:
#include <map>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "rpcSrv.h"
#include "winword.h"
#include "inputLangChange.h"
#include "typedCharacter.h"
#include "IA2Support.h"
#include "ia2LiveRegions.h"
#include "log.h"
#include <common/log.h>
#include "gdiHooks.h"
#include "nvdaHelperRemote.h"
#include "inProcess.h"
Expand All @@ -45,11 +44,9 @@ void inProcess_initialize() {
inputLangChange_inProcess_initialize();
winword_inProcess_initialize();
gdiHooks_inProcess_initialize();
rpcSrv_inProcess_initialize();
}

void inProcess_terminate() {
rpcSrv_inProcess_terminate();
gdiHooks_inProcess_terminate();
winword_inProcess_terminate();
inputLangChange_inProcess_terminate();
Expand Down
Loading

0 comments on commit 54b96a9

Please sign in to comment.