OSDN Git Service

A bit of a re-write to nvdahelper injection and rpc code to allow NVDA access to...
authorMichael Curran <mick@kulgan.net>
Mon, 16 Apr 2012 01:24:15 +0000 (11:24 +1000)
committerMichael Curran <mick@kulgan.net>
Mon, 16 Apr 2012 01:24:15 +0000 (11:24 +1000)
Specific changes:
   * On win8 nvdaHelperLocal registers the nvdaController and nvdaControllerInternal interfaces using the new RpcServerRegisterIf3, allowing the use of a security descriptor which specifically allows access from AppContainer integrity processes. However as the security descriptor overrides any defaults, it also for now allows access from any authenticated users  -- this could be tightened up.
* nvdaInprocUtils now has two new methods: registerNVDAProcess and unregisterNVDAProcess, plus an rundown function for the context handle registerNVDAProcess provides. When injection is all initialized, NVDA can call registerNVDAprocess and when it wants the inproc code to terminate it can call unregisterNVDAProcess, or just break the connection or exit. registerNVDAProcess sets an nvdaUnregisteredEvent to nonSignaled, which the inproc code uses to wait on. UnregisterNVDAProcess (also called by the rundown if the connection breaks) sets this event to signaled to instruct the inproc code to terminate.
* nvdaHelperRemote: remove the injectionDoneEvent and any code that waited on it or the NVDA process. Its no longer needed. Note that this did not work in AppContainer processes anyway unless a specific security descriptor was set for both the NVDA process and the injectionDoneEvent.
* nvdaHelperRemote: When registering its RPC interfaces (nvdaInprocUtils, displayModel and VBuf), use RpcUseProtSeq rather than RpcUseProtSeqEp so as to listen on a dynamic endpoint rather than a well-known one. AppContainer processes mangled the endpoint name anyway. Publish correct dynamic endpoints for each interface along with a generated UUID specific to this instance of the inproc code, using RpcEpRegister, so that NVDA can locate this processes endpoint by using the interface and generated UUID. Finally also call a new nvdaControllerInternal method: requestRegistration to ask NVDA to register with this inproc code before the call returns.
* nvdaHelperLocal: rename createConnection to createRemoteBindingHandle (which is more true) and also set a security descriptor on the created binding handle to allow an AppContainer server to talk back to this client -- could be tightened up. createRemoteBindingHandle also now takes a uuid parameter which it uses when creating the binding handle so its able to locate the correct endpoint.
* nvdaControllerInternal: add a new requestRegistration method, which takes a UUID string which can be used to locate the caller's own inproc rpc interfaces. The implementation of requestRegistration uses the UUID to create a bindingHandle for remote access to the inproc code using createRemoteBindingHandle, storing it on the appModule which it creates if necessary. It also calls registerNVDAProcess so as to retreave a context handle from the inproc code that if broken will force the inproc code to terminate. This is also stored on the appModule.
* AppModule.terminate: If a registrationhandle exists, destroy it with rpcSsDestroyClientContext. If a bindingHandle exists then free it with RpcBindingFree. Destroying both of these is enough to ensure the connection with the inproc code breaks and therefore the rundown for the registration will run and inproc code will be terminated. This is safer than specifically calling unregisterNVDAProcess which could freee or may fail if the remote process is busy or shutting down or is dead etc.
* core.initialize: initialize appModuleHandler before nvdaHelper as nvdahelper.nvdaControllerInternal_requestRegistration, which can happen quite quick, needs the ability to create an appModule.

17 files changed:
nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.acf
nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.idl
nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.acf
nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.idl
nvdaHelper/local/nvdaControllerInternal.c
nvdaHelper/local/nvdaHelperLocal.cpp
nvdaHelper/local/nvdaHelperLocal.def
nvdaHelper/local/nvdaHelperLocal.h
nvdaHelper/local/rpcSrv.cpp
nvdaHelper/local/sconscript
nvdaHelper/remote/inProcess.cpp
nvdaHelper/remote/injection.cpp
nvdaHelper/remote/rpcSrv.cpp
nvdaHelper/remote/rpcSrv.h
source/NVDAHelper.py
source/appModuleHandler.py
source/core.py

index 15fa72f..d1187d7 100644 (file)
@@ -16,7 +16,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
        implicit_handle(handle_t nvdaControllerInternalBindingHandle)\r
 ]\r
 interface NvdaControllerInternal {\r
-       [fault_status,comm_status] getNVDAProcessID();\r
+       [fault_status,comm_status] requestRegistration();\r
        [fault_status,comm_status] inputLangChangeNotify();\r
        [fault_status,comm_status] typedCharacterNotify();\r
        [fault_status,comm_status] displayModelTextChangeNotify();\r
index 06bec77..4628675 100644 (file)
@@ -35,11 +35,7 @@ cpp_quote("*/")
 ]\r
 interface NvdaControllerInternal {\r
 \r
-/**\r
- * Gets the process ID of NVDA.\r
- * @param pProcessID memory where the  retreaved process ID can be placed.\r
- */\r
-       error_status_t __stdcall getNVDAProcessID([out] long* pProcessID); \r
+       error_status_t __stdcall requestRegistration([in,string] const wchar_t* uuidString);\r
 \r
 /**\r
  * Notifies NVDA that the keyboard layout has changed for this thread. \r
index ba2ba93..4cacb9b 100644 (file)
@@ -12,7 +12,10 @@ This license can be found at:
 http://www.gnu.org/licenses/old-licenses/gpl-2.0.html\r
 */\r
 \r
+[strict_context_handle]\r
 interface NvdaInProcUtils {\r
+       [fault_status,comm_status] registerNVDAProcess();\r
+       [fault_status,comm_status] unregisterNVDAProcess();\r
        [fault_status,comm_status] winword_expandToLine();\r
        [fault_status,comm_status] winword_getTextInRange();\r
 }\r
index 8ccddb9..045c240 100644 (file)
@@ -38,6 +38,11 @@ import "oaidl.idl";
 ]\r
 interface NvdaInProcUtils {\r
 \r
+       typedef [context_handle] void* nvdaRegistrationHandle_t;\r
+\r
+       error_status_t registerNVDAProcess([in] handle_t bindingHandle, [out] nvdaRegistrationHandle_t* registrationhandle);\r
+       error_status_t unregisterNVDAProcess([in,out] nvdaRegistrationHandle_t* registrationhandle);\r
+\r
        error_status_t winword_expandToLine([in] const long windowHandle, [in] const int offset, [out] int* lineStart, [out] int* lineEnd);\r
 \r
        error_status_t winword_getTextInRange([in] const long windowHandle, [in] const int startOffset, [in] const int endOffset, [in] const long formatConfig, [out] BSTR* text); \r
index f84aee1..5f9a85b 100644 (file)
@@ -14,9 +14,9 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 \r
 #include "nvdaControllerInternal.h"\r
 \r
-error_status_t __stdcall nvdaControllerInternal_getNVDAProcessID(long* pProcessID) {\r
-       *pProcessID=GetCurrentProcessId();\r
-       return RPC_S_OK;\r
+error_status_t(__stdcall *_nvdaControllerInternal_requestRegistration)(const wchar_t*);\r
+error_status_t __stdcall nvdaControllerInternal_requestRegistration(const wchar_t* uuidString) {\r
+       return _nvdaControllerInternal_requestRegistration(uuidString);\r
 }\r
 \r
 error_status_t(__stdcall *_nvdaControllerInternal_inputLangChangeNotify)(const long, const unsigned long, const wchar_t*);\r
index 12a0ab7..31f8270 100644 (file)
@@ -15,6 +15,8 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include <cstdio>\r
 #include <sstream>\r
 #include <algorithm>\r
+#include <rpc.h>\r
+#include <sddl.h>\r
 #include "nvdaHelperLocal.h"\r
 #include "dllImportTableHooks.h"\r
 #include "rpcsrv.h"\r
@@ -22,22 +24,45 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 DllImportTableHooks* oleaccHooks = NULL;\r
 DllImportTableHooks* uiaCoreHooks = NULL;\r
 \r
-handle_t createConnection(int processID) {\r
+typedef struct _RPC_SECURITY_QOS_V5_W {\r
+  unsigned long Version;\r
+  unsigned long Capabilities;\r
+  unsigned long IdentityTracking;\r
+  unsigned long ImpersonationType;\r
+  unsigned long AdditionalSecurityInfoType;\r
+  union \r
+      {\r
+      RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials;\r
+      } u;\r
+  void *Sid;\r
+  unsigned int EffectiveOnly;\r
+  void *ServerSecurityDescriptor;\r
+} RPC_SECURITY_QOS_V5_W, *PRPC_SECURITY_QOS_V5_W;\r
+\r
+handle_t createRemoteBindingHandle(wchar_t* uuidString) {\r
        RPC_STATUS rpcStatus;\r
-       std::wostringstream addr;\r
-       addr<<L"ncalrpc:[nvdaHelperRemote_"<<processID<<L"]";\r
+       RPC_WSTR stringBinding;\r
+       if((rpcStatus=RpcStringBindingCompose((RPC_WSTR)uuidString,(RPC_WSTR)L"ncalrpc",NULL,NULL,NULL,&stringBinding))!=RPC_S_OK) {\r
+               fprintf(stderr,"RpcStringBindingCompose failed, rpc code 0X%X\n",rpcStatus);\r
+               return NULL;\r
+       }\r
        handle_t bindingHandle;\r
-       if((rpcStatus=RpcBindingFromStringBinding((RPC_WSTR)(addr.str().c_str()),&bindingHandle))!=RPC_S_OK) {\r
+       if((rpcStatus=RpcBindingFromStringBinding(stringBinding,&bindingHandle))!=RPC_S_OK) {\r
                fprintf(stderr,"Error creating binding handle from string binding, rpc code 0X%X\n",rpcStatus);\r
                return NULL;\r
        } \r
+       PSECURITY_DESCRIPTOR psd=NULL;\r
+       ULONG size;\r
+       if(!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:(A;;GA;;;AU)(A;;GA;;;AC)",SDDL_REVISION_1,&psd,&size)) {\r
+               return NULL;\r
+       }\r
+       RPC_SECURITY_QOS_V5_W securityQos={5,0,0,0,0,NULL,NULL,0,psd};\r
+       if(RpcBindingSetAuthInfoEx(bindingHandle,NULL,RPC_C_AUTHN_LEVEL_DEFAULT,RPC_C_AUTHN_DEFAULT,NULL,0,(RPC_SECURITY_QOS*)&securityQos)!=RPC_S_OK) {\r
+               return NULL;\r
+       }\r
        return bindingHandle;\r
 }\r
 \r
-void destroyConnection(handle_t bindingHandle) {\r
-       RpcBindingFree(&bindingHandle);\r
-}\r
-\r
 bool shouldCancelSendMessage;\r
 const UINT CANCELSENDMESSAGE_CHECK_INTERVAL = 400;\r
 \r
index 1c65929..424265a 100644 (file)
@@ -1,12 +1,13 @@
 EXPORTS\r
-       createConnection\r
-       destroyConnection\r
+       createRemoteBindingHandle\r
        cancellableSendMessageTimeout\r
        cancelSendMessage\r
        _notifySendMessageCancelled\r
        nvdaHelperLocal_initialize\r
        nvdaHelperLocal_terminate\r
        generateBeep\r
+       nvdaInProcUtils_registerNVDAProcess\r
+       nvdaInProcUtils_unregisterNVDAProcess\r
        nvdaInProcUtils_winword_expandToLine\r
        nvdaInProcUtils_winword_getTextInRange\r
        VBuf_createBuffer\r
@@ -25,6 +26,7 @@ EXPORTS
        VBuf_locateControlFieldNodeAtOffset\r
        VBuf_locateTextFieldNodeAtOffset\r
        VBuf_setSelectionOffsets\r
+       _nvdaControllerInternal_requestRegistration\r
        _nvdaControllerInternal_displayModelTextChangeNotify\r
        _nvdaControllerInternal_inputLangChangeNotify\r
        _nvdaControllerInternal_logMessage\r
index 9d4a014..a9ee2fc 100755 (executable)
@@ -16,8 +16,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #define NVDAHELPERLOCAL_H\r
 #include <rpc.h>\r
 \r
-handle_t createConnection(int processID);\r
-void destroyConnection(handle_t bindingHandle);\r
+handle_t createRemoteBindingHandle(wchar_t* uuidString);\r
 LRESULT cancellableSendMessageTimeout(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult);\r
 void cancelSendMessage();\r
 void nvdaHelperLocal_initialize();\r
index 9b9ee04..ef8ae15 100644 (file)
@@ -15,6 +15,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include <cstdio>\r
 #include <sstream>\r
 #include <rpc.h>\r
+#include <sddl.h>\r
 #include "nvdaController.h"\r
 #include "nvdaControllerInternal.h"\r
 #include <common/winIPCUtils.h>\r
@@ -22,6 +23,8 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 \r
 using namespace std;\r
 \r
+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*);\r
+\r
 RPC_IF_HANDLE availableInterfaces[]={\r
        nvdaController_NvdaController_v1_0_s_ifspec,\r
        nvdaControllerInternal_NvdaControllerInternal_v1_0_s_ifspec\r
@@ -40,22 +43,41 @@ void __RPC_USER midl_user_free(void* p) {
 \r
 RPC_STATUS startServer() {\r
        RPC_STATUS status;\r
-       //Set the protocol\r
        wchar_t desktopSpecificNamespace[64];\r
        generateDesktopSpecificNamespace(desktopSpecificNamespace,ARRAYSIZE(desktopSpecificNamespace));\r
-       wstringstream endpointStringStream;\r
-       endpointStringStream<<L"NvdaCtlr."<<desktopSpecificNamespace;\r
-       status=RpcServerUseProtseqEp((RPC_WSTR)L"ncalrpc",RPC_C_PROTSEQ_MAX_REQS_DEFAULT,(RPC_WSTR)(endpointStringStream.str().c_str()),NULL);\r
+       wstring endpointString=L"NvdaCtlr.";\r
+       endpointString+=desktopSpecificNamespace;\r
+       //On Windows 8 the new rpcServerRegisterIf3 must be used along with a security descriptor allowing appContainer access\r
+       HANDLE rpcrt4Handle=GetModuleHandle(L"rpcrt4.dll");\r
+       RpcServerRegisterIf3_functype RpcServerRegisterIf3=(RpcServerRegisterIf3_functype)GetProcAddress((HMODULE)rpcrt4Handle,"RpcServerRegisterIf3");\r
+       PSECURITY_DESCRIPTOR psd=NULL;\r
+       ULONG size;\r
+       if(RpcServerRegisterIf3) {\r
+               if(!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:(A;;GA;;;AU)(A;;GA;;;AC)",SDDL_REVISION_1,&psd,&size)) {\r
+                       return GetLastError();\r
+               }\r
+               if(!psd) return -1;\r
+       }\r
+       status=RpcServerUseProtseqEp((RPC_WSTR)L"ncalrpc",RPC_C_PROTSEQ_MAX_REQS_DEFAULT,(RPC_WSTR)(endpointString.c_str()),psd);\r
        //We can ignore the error where the endpoint is already set\r
        if(status!=RPC_S_OK&&status!=RPC_S_DUPLICATE_ENDPOINT) {\r
                return status;\r
        }\r
        //Register the interfaces\r
-       for(int i=0;i<ARRAYSIZE(availableInterfaces);i++) {\r
-               if((status=RpcServerRegisterIf(availableInterfaces[i],NULL,NULL))!=RPC_S_OK) {\r
-                       return status;\r
+       if(RpcServerRegisterIf3) { //Windows 8\r
+               for(int i=0;i<ARRAYSIZE(availableInterfaces);i++) {\r
+                       if((status=RpcServerRegisterIf3(availableInterfaces[i],NULL,NULL,RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,RPC_C_LISTEN_MAX_CALLS_DEFAULT,0,NULL,psd))!=RPC_S_OK) {\r
+                               return status;\r
+                       }\r
+               }\r
+       } else { // Pre Windows 8\r
+               for(int i=0;i<ARRAYSIZE(availableInterfaces);i++) {\r
+                       if((status=RpcServerRegisterIf(availableInterfaces[i],NULL,NULL))!=RPC_S_OK) {\r
+                               return status;\r
+                       }\r
                }\r
        }\r
+       LocalFree(psd);\r
        //Start listening\r
        if((status=RpcServerListen(1,RPC_C_LISTEN_MAX_CALLS_DEFAULT,TRUE))!=RPC_S_OK) {\r
                return status;\r
index 4ffe9e6..d3448b7 100644 (file)
@@ -86,6 +86,7 @@ localLib=env.SharedLibrary(
                "textUtils.cpp",\r
        ],\r
        LIBS=[\r
+               "advapi32.lib",\r
                "user32",\r
                "usp10",\r
                "ole32",\r
index 7867f52..271a5e0 100755 (executable)
@@ -18,7 +18,6 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include <map>\r
 #define WIN32_LEAN_AND_MEAN \r
 #include <windows.h>\r
-#include "rpcSrv.h"\r
 #include "winword.h"\r
 #include "inputLangChange.h"\r
 #include "typedCharacter.h"\r
@@ -45,11 +44,9 @@ void inProcess_initialize() {
        inputLangChange_inProcess_initialize();\r
        winword_inProcess_initialize();\r
        gdiHooks_inProcess_initialize();\r
-       rpcSrv_inProcess_initialize();\r
 }\r
 \r
 void inProcess_terminate() {\r
-       rpcSrv_inProcess_terminate();\r
        gdiHooks_inProcess_terminate();\r
        winword_inProcess_terminate();\r
        inputLangChange_inProcess_terminate();\r
index cc3442a..78026bb 100644 (file)
@@ -18,6 +18,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #define WIN32_LEAN_AND_MEAN \r
 #include <windows.h>\r
 #include <shlwapi.h>\r
+#include <sddl.h>\r
 #include "log.h"\r
 #include "ia2Support.h"\r
 #include "apiHook.h"\r
@@ -28,6 +29,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include "dllmain.h"\r
 #include "nvdaHelperRemote.h"\r
 #include "inProcess.h"\r
+#include "rpcSrv.h"\r
 \r
 using namespace std;\r
 \r
@@ -101,79 +103,63 @@ DWORD WINAPI inprocMgrThreadFunc(LPVOID data) {
        HINSTANCE tempHandle=NULL;\r
        if(!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,(LPCTSTR)dllHandle,&tempHandle)) {\r
                LOG_ERROR(L"GetModuleHandleEx failed, GetLastError returned "<<GetLastError());\r
+               ReleaseMutex(threadMutex);\r
                return 0;\r
        }\r
        nhAssert(dllHandle==tempHandle);\r
-       //Try to open handles to both the injectionDone event and NVDA's process handle\r
-               HANDLE waitHandles[2]={0};\r
-       long nvdaProcessID=0;\r
-       nvdaControllerInternal_getNVDAProcessID(&nvdaProcessID);\r
-       if(nvdaProcessID>0) {\r
-               waitHandles[0]=OpenProcess(SYNCHRONIZE,FALSE,nvdaProcessID);\r
-               wstringstream s;\r
-               s<<L"nvdaHelperRemote_injectionDoneEvent_"<<desktopSpecificNamespace;\r
-               waitHandles[1]=OpenEvent(SYNCHRONIZE,FALSE,s.str().c_str());\r
+       //Register for all winEvents in this process.\r
+       inprocWinEventHookID=SetWinEventHook(EVENT_MIN,EVENT_MAX,dllHandle,inproc_winEventCallback,GetCurrentProcessId(),0,WINEVENT_INCONTEXT);\r
+       if(inprocWinEventHookID==0) {\r
+               LOG_ERROR(L"SetWinEventHook failed");\r
        }\r
-       //As long as we have successfully retreaved handles for NVDA's process and the event, then go on and initialize, wait and terminate.\r
-       if(waitHandles[0]&&waitHandles[1]) {\r
-               //Register for all winEvents in this process.\r
-               inprocWinEventHookID=SetWinEventHook(EVENT_MIN,EVENT_MAX,dllHandle,inproc_winEventCallback,GetCurrentProcessId(),0,WINEVENT_INCONTEXT);\r
-               if(inprocWinEventHookID==0) {\r
-                       LOG_ERROR(L"SetWinEventHook failed");\r
+       //Initialize API hooking\r
+       apiHook_initialize();\r
+//Fore secure mode NVDA process, hook OpenClipboard to disable usage of the clipboard\r
+if(isSecureModeNVDAProcess) real_OpenClipboard=apiHook_hookFunction_safe("USER32.dll",OpenClipboard,fake_OpenClipboard);\r
+       //Initialize in-process subsystems\r
+       inProcess_initialize();\r
+       //Enable all registered API hooks\r
+       apiHook_enableHooks();\r
+       //Initialize our rpc server interfaces and request registration with NVDA\r
+       rpcSrv_initialize();\r
+       //Notify injection_winEventCallback (who started our thread) that we're past initialization\r
+       SetEvent((HANDLE)data);\r
+       //Wait until nvda unregisters\r
+       #ifndef NDEBUG\r
+       Beep(660,75);\r
+       #endif\r
+       // Even though we only registered for in-context winEvents, we may still receive some out-of-context events; e.g. console events.\r
+       // Therefore, we must have a message loop.\r
+       // Otherwise, any out-of-context events will cause major lag which increases over time.\r
+       do {\r
+               // Consume and handle all pending messages.\r
+               MSG msg;\r
+               while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {\r
+                       TranslateMessage(&msg);\r
+                       DispatchMessage(&msg);\r
                }\r
-               //Initialize API hooking\r
-               apiHook_initialize();\r
-       //Fore secure mode NVDA process, hook OpenClipboard to disable usage of the clipboard\r
-       if(isSecureModeNVDAProcess) real_OpenClipboard=apiHook_hookFunction_safe("USER32.dll",OpenClipboard,fake_OpenClipboard);\r
-               //Initialize in-process subsystems\r
-               inProcess_initialize();\r
-               //Enable all registered API hooks\r
-               apiHook_enableHooks();\r
-               //Notify injection_winEventCallback (who started our thread) that we're past initialization\r
-               SetEvent((HANDLE)data);\r
-               //Wait till either the injection done event is set, or NVDA's process dies\r
-               #ifndef NDEBUG\r
-               Beep(660,75);\r
-               #endif\r
-               // Even though we only registered for in-context winEvents, we may still receive some out-of-context events; e.g. console events.\r
-               // Therefore, we must have a message loop.\r
-               // Otherwise, any out-of-context events will cause major lag which increases over time.\r
-               do {\r
-                       // Consume and handle all pending messages.\r
-                       MSG msg;\r
-                       while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {\r
-                               TranslateMessage(&msg);\r
-                               DispatchMessage(&msg);\r
-                       }\r
-               } while(MsgWaitForMultipleObjects(2,waitHandles,FALSE,INFINITE,QS_ALLINPUT)==WAIT_OBJECT_0+2);\r
-               nhAssert(inprocMgrThreadHandle);\r
-               inprocThreadsLock.acquire();\r
-               CloseHandle(inprocMgrThreadHandle);\r
-               inprocMgrThreadHandle=NULL;\r
-               inprocThreadsLock.release();\r
-               #ifndef NDEBUG\r
-               Beep(1320,75);\r
-               #endif\r
-               //Unregister and terminate API hooks\r
-               apiHook_terminate();\r
-               //Terminate all in-process subsystems.\r
-               inProcess_terminate();\r
-               //Unregister winEvents for this process\r
-               if(inprocWinEventHookID) { \r
-                       UnhookWinEvent(inprocWinEventHookID);\r
-                       inprocWinEventHookID=0;\r
-               }\r
-               //Unregister any windows hooks registered so far\r
-               killRunningWindowsHooks();\r
-       } else {\r
-               nhAssert(inprocMgrThreadHandle);\r
-               inprocThreadsLock.acquire();\r
-               CloseHandle(inprocMgrThreadHandle);\r
-               inprocMgrThreadHandle=NULL;\r
-               inprocThreadsLock.release();\r
+       } while(MsgWaitForMultipleObjects(1,&nvdaUnregisteredEvent,FALSE,INFINITE,QS_ALLINPUT)==WAIT_OBJECT_0+1);\r
+       nhAssert(inprocMgrThreadHandle);\r
+       inprocThreadsLock.acquire();\r
+       CloseHandle(inprocMgrThreadHandle);\r
+       inprocMgrThreadHandle=NULL;\r
+       inprocThreadsLock.release();\r
+       #ifndef NDEBUG\r
+       Beep(1320,75);\r
+       #endif\r
+       //Terminate our RPC server interfaces\r
+       rpcSrv_terminate();\r
+       //Unregister and terminate API hooks\r
+       apiHook_terminate();\r
+       //Terminate all in-process subsystems.\r
+       inProcess_terminate();\r
+       //Unregister winEvents for this process\r
+       if(inprocWinEventHookID) { \r
+               UnhookWinEvent(inprocWinEventHookID);\r
+               inprocWinEventHookID=0;\r
        }\r
-       if(waitHandles[0]) CloseHandle(waitHandles[0]);\r
-       if(waitHandles[1]) CloseHandle(waitHandles[1]);\r
+       //Unregister any windows hooks registered so far\r
+       killRunningWindowsHooks();\r
        //Release and close the thread mutex\r
        ReleaseMutex(threadMutex);\r
        CloseHandle(threadMutex);\r
@@ -269,7 +255,6 @@ DWORD WINAPI outprocMgrThreadFunc(LPVOID data) {
 HANDLE outprocMgrThreadHandle=NULL;\r
 DWORD outprocMgrThreadID=0;\r
 BOOL outprocInitialized=FALSE;\r
-HANDLE injectionDoneEvent=NULL;\r
 \r
 /**\r
  * Initializes the out-of-process code for NVDAHelper \r
@@ -286,14 +271,6 @@ BOOL injection_initialize(int secureMode) {
                MessageBox(NULL,L"Error initializing IA2 support",L"nvdaHelperRemote (injection_initialize)",0);\r
                return FALSE;\r
        }\r
-       nhAssert(!injectionDoneEvent);\r
-       {\r
-               wstringstream s;\r
-               s<<L"nvdaHelperRemote_injectionDoneEvent_"<<desktopSpecificNamespace;\r
-               injectionDoneEvent=CreateEvent(NULL,TRUE,FALSE,s.str().c_str());\r
-       }\r
-       nhAssert(injectionDoneEvent);\r
-       ResetEvent(injectionDoneEvent);\r
        outprocMgrThreadHandle=CreateThread(NULL,0,outprocMgrThreadFunc,NULL,0,&outprocMgrThreadID);\r
        outprocInitialized=TRUE;\r
        return TRUE;\r
@@ -310,10 +287,6 @@ BOOL injection_terminate() {
        }\r
        outprocMgrThreadHandle=NULL;\r
        outprocMgrThreadID=0;\r
-       nhAssert(injectionDoneEvent);\r
-       SetEvent(injectionDoneEvent);\r
-       CloseHandle(injectionDoneEvent);\r
-       injectionDoneEvent=NULL;\r
        outprocInitialized=FALSE;\r
        return TRUE;\r
 }\r
@@ -332,10 +305,13 @@ BOOL WINAPI DllMain(HINSTANCE hModule,DWORD reason,LPVOID lpReserved) {
                PathRemoveFileSpec(dllDirectory);\r
                generateDesktopSpecificNamespace(desktopSpecificNamespace,ARRAYSIZE(desktopSpecificNamespace));\r
                //Initialize some needed RPC binding handles.\r
-               wstringstream s;\r
-               s<<L"ncalrpc:[NvdaCtlr."<<desktopSpecificNamespace<<L"]";\r
-               RpcBindingFromStringBinding((RPC_WSTR)(s.str().c_str()),&nvdaControllerBindingHandle);\r
-               RpcBindingFromStringBinding((RPC_WSTR)(s.str().c_str()),&nvdaControllerInternalBindingHandle);\r
+               wstring endpoint=L"NvdaCtlr.";\r
+               endpoint+=desktopSpecificNamespace;\r
+               RPC_WSTR stringBinding;\r
+               RpcStringBindingCompose(NULL,(RPC_WSTR)L"ncalrpc",NULL,(RPC_WSTR)(endpoint.c_str()),NULL,&stringBinding);\r
+               RpcBindingFromStringBinding(stringBinding,&nvdaControllerBindingHandle);\r
+               RpcBindingFromStringBinding(stringBinding,&nvdaControllerInternalBindingHandle);\r
+               RpcStringFree(&stringBinding);\r
        } else if(reason==DLL_PROCESS_DETACH) {\r
                #ifndef NDEBUG\r
                Beep(1760,75);\r
index 3f40eda..0a03a4d 100755 (executable)
@@ -15,13 +15,17 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include <cstdio>\r
 #include <sstream>\r
 #include <rpc.h>\r
+#include <sddl.h>\r
 #include "nvdaControllerInternal.h"\r
 #include "log.h"\r
 #include "vbufRemote.h"\r
 #include "displayModelRemote.h"\r
-#include "nvdaInProcUtils.h"\r
+#include "NvdaInProcUtils.h"\r
+#include "nvdaControllerInternal.h"\r
 #include "rpcSrv.h"\r
 \r
+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*);\r
+\r
 RPC_IF_HANDLE availableInterfaces[]={\r
        nvdaInProcUtils_NvdaInProcUtils_v1_0_s_ifspec,\r
        displayModelRemote_DisplayModel_v1_0_s_ifspec,\r
@@ -38,28 +42,75 @@ void __RPC_USER midl_user_free(void* p) {
        free(p);\r
 }\r
 \r
-void rpcSrv_inProcess_initialize() {\r
+HANDLE nvdaUnregisteredEvent=NULL;\r
+RPC_BINDING_VECTOR* bindingVector;\r
+UUID nvdaInprocUuid;\r
+\r
+RPC_STATUS rpcSrv_initialize() {\r
+       nvdaUnregisteredEvent=CreateEvent(NULL,TRUE,false,NULL);\r
        RPC_STATUS status;\r
        //Set the protocol\r
-       std::wostringstream endPoint;\r
-       endPoint<<L"nvdaHelperRemote_"<<GetCurrentProcessId();\r
-       status=RpcServerUseProtseqEp((RPC_WSTR)L"ncalrpc",RPC_C_PROTSEQ_MAX_REQS_DEFAULT,(RPC_WSTR)(endPoint.str().c_str()),NULL);\r
+       status=RpcServerUseProtseq((RPC_WSTR)L"ncalrpc",RPC_C_PROTSEQ_MAX_REQS_DEFAULT,NULL);\r
+       //We can ignore the error where the endpoint is already set\r
        if(status!=RPC_S_OK&&status!=RPC_S_DUPLICATE_ENDPOINT) {\r
-               LOG_ERROR(L"RpcServerUseProtseqEp failed with status "<<status);\r
+               LOG_ERROR(L"Unable to use RPC endPoint. RPC error "<<status); \r
+               return status;\r
+       }\r
+       if((status=RpcServerInqBindings(&bindingVector))!=RPC_S_OK) {\r
+               LOG_ERROR(L"RpcServerInqBindings failed with status "<<status);\r
+               return status;\r
        }\r
+       UuidCreate(&nvdaInprocUuid);\r
+       UUID_VECTOR nvdaInprocUuidVector={1,&nvdaInprocUuid};\r
        //Register the interfaces\r
        for(int i=0;i<ARRAYSIZE(availableInterfaces);++i) {\r
                if((status=RpcServerRegisterIfEx(availableInterfaces[i],NULL,NULL,RPC_IF_AUTOLISTEN,RPC_C_LISTEN_MAX_CALLS_DEFAULT,NULL))!=RPC_S_OK) {\r
                        LOG_ERROR(L"RpcServerRegisterIfEx for interface at index "<<i<<L" failed with status "<<status);\r
+                       continue;\r
                }\r
+               if((status=RpcEpRegister(availableInterfaces[i],bindingVector,&nvdaInprocUuidVector,(RPC_WSTR)L"NVDAHelperRemote interface"))!=RPC_S_OK) {\r
+                       LOG_ERROR(L"RpcEpRegister failed for interface at index "<<i<<L" with status "<<status);\r
+                       continue;\r
+               }\r
+       }\r
+       RPC_WSTR uuidString;\r
+       UuidToString(&nvdaInprocUuid,&uuidString);\r
+       RpcBindingSetObject(nvdaControllerInternalBindingHandle,&nvdaInprocUuid);\r
+       if((status=nvdaControllerInternal_requestRegistration((wchar_t*)uuidString))!=RPC_S_OK) {\r
+               LOG_ERROR(L"nvdaControllerInternal_requestRegistration failed with status "<<status);\r
        }\r
+       RpcStringFree(&uuidString);\r
+       return status;\r
 }\r
 \r
-void rpcSrv_inProcess_terminate() {\r
+void rpcSrv_terminate() {\r
        RPC_STATUS status;\r
+       CloseHandle(nvdaUnregisteredEvent);\r
+       nvdaUnregisteredEvent=NULL;\r
+       UUID_VECTOR nvdaInprocUuidVector={1,&nvdaInprocUuid};\r
        for(int i=0;i<ARRAYSIZE(availableInterfaces);++i) {\r
+               if((status=RpcEpUnregister(availableInterfaces[i],bindingVector,&nvdaInprocUuidVector))!=RPC_S_OK) {\r
+                       LOG_ERROR(L"RpcEpUnregister failed for interface at index "<<i<<L" with status "<<status);\r
+               }\r
                if((status=RpcServerUnregisterIfEx(availableInterfaces[i],NULL,1))!=RPC_S_OK) {\r
                        LOG_ERROR(L"RpcServerUnregisterIfEx for interface at index "<<i<<L" failed with status "<<status);\r
                }\r
        }\r
+       RpcBindingVectorFree(&bindingVector);\r
+}\r
+\r
+error_status_t nvdaInProcUtils_registerNVDAProcess(handle_t bindingHandle, nvdaRegistrationHandle_t* registrationHandle) {\r
+       ResetEvent(nvdaUnregisteredEvent);\r
+       *registrationHandle=&nvdaUnregisteredEvent;\r
+       return RPC_S_OK;\r
+}\r
+\r
+error_status_t nvdaInProcUtils_unregisterNVDAProcess(nvdaRegistrationHandle_t* registrationHandle) {\r
+       SetEvent(nvdaUnregisteredEvent);\r
+       *registrationHandle=NULL;\r
+       return RPC_S_OK;\r
+}\r
+\r
+void __RPC_USER nvdaRegistrationHandle_t_rundown(nvdaRegistrationHandle_t registrationHandle) {\r
+       nvdaInProcUtils_unregisterNVDAProcess(&registrationHandle);\r
 }\r
index 839bfeb..73258d7 100644 (file)
@@ -15,7 +15,9 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #ifndef RPCSRV_H\r
 #define RPCSRV_H\r
 \r
-void rpcSrv_inProcess_initialize();\r
-void rpcSrv_inProcess_terminate();\r
+extern HANDLE nvdaUnregisteredEvent;\r
+\r
+RPC_STATUS rpcSrv_initialize();\r
+void rpcSrv_terminate();\r
 \r
 #endif\r
index 23880d1..bfc7493 100755 (executable)
@@ -1,9 +1,11 @@
 import os\r
+import sys\r
 import _winreg\r
 import msvcrt\r
 import winKernel\r
 \r
 from ctypes import *\r
+from ctypes.wintypes import *\r
 from comtypes import BSTR\r
 import winUser\r
 import eventHandler\r
@@ -68,6 +70,31 @@ def _lookupKeyboardLayoutNameWithHexString(layoutString):
                log.debugWarning("Could not find reg value 'Layout Text' for reg key %s"%layoutString)\r
                return None\r
 \r
+@WINFUNCTYPE(c_long,c_wchar_p)\r
+def nvdaControllerInternal_requestRegistration(uuidString):\r
+       pid=c_long()\r
+       windll.rpcrt4.I_RpcBindingInqLocalClientPID(None,byref(pid))\r
+       pid=pid.value\r
+       if not pid:\r
+               log.error("Could not get process ID for RPC call")\r
+               return -1;\r
+       import appModuleHandler\r
+       mod=appModuleHandler.getAppModuleFromProcessID(pid)\r
+       bindingHandle=c_void_p()\r
+       bindingHandle.value=localLib.createRemoteBindingHandle(uuidString)\r
+       if not bindingHandle: \r
+               log.error("Could not bind to inproc rpc server for pid %d"%pid)\r
+               return -1\r
+       registrationHandle=c_long()\r
+       res=localLib.nvdaInProcUtils_registerNVDAProcess(bindingHandle,byref(registrationHandle))\r
+       if res!=0 or not registrationHandle:\r
+               log.error("Could not register NVDA with inproc rpc server for pid %d, res %d, registrationHandle %s"%(pid,res,registrationHandle))\r
+               windll.rpcrt4.RpcBindingFree(byref(bindingHandle))\r
+               return -1\r
+       mod.helperLocalBindingHandle=bindingHandle\r
+       mod._inprocRegistrationHandle=registrationHandle\r
+       return 0\r
+\r
 @WINFUNCTYPE(c_long,c_long,c_long,c_long,c_long,c_long)\r
 def nvdaControllerInternal_displayModelTextChangeNotify(hwnd, left, top, right, bottom):\r
        import displayModel\r
@@ -167,6 +194,7 @@ def initialize():
                ("nvdaController_speakText",nvdaController_speakText),\r
                ("nvdaController_cancelSpeech",nvdaController_cancelSpeech),\r
                ("nvdaController_brailleMessage",nvdaController_brailleMessage),\r
+               ("nvdaControllerInternal_requestRegistration",nvdaControllerInternal_requestRegistration),\r
                ("nvdaControllerInternal_inputLangChangeNotify",nvdaControllerInternal_inputLangChangeNotify),\r
                ("nvdaControllerInternal_typedCharacterNotify",nvdaControllerInternal_typedCharacterNotify),\r
                ("nvdaControllerInternal_displayModelTextChangeNotify",nvdaControllerInternal_displayModelTextChangeNotify),\r
@@ -174,8 +202,9 @@ def initialize():
        ]:\r
                try:\r
                        _setDllFuncPointer(localLib,"_%s"%name,func)\r
-               except AttributeError:\r
-                       log.error("nvdaHelperLocal function pointer for %s could not be found, possibly old nvdaHelperLocal dll"%name)\r
+               except AttributeError as e:\r
+                       log.error("nvdaHelperLocal function pointer for %s could not be found, possibly old nvdaHelperLocal dll"%name,exc_info=True)\r
+                       raise e\r
        localLib.nvdaHelperLocal_initialize()\r
        generateBeep=localLib.generateBeep\r
        generateBeep.argtypes=[c_char_p,c_float,c_uint,c_ubyte,c_ubyte]\r
index 4a8bcbf..b5326d3 100644 (file)
@@ -196,13 +196,14 @@ class AppModule(baseObject.ScriptableObject):
                #: The ID of the process this appModule is for.\r
                #: @type: int\r
                self.processID=processID\r
-               self.helperLocalBindingHandle=NVDAHelper.localLib.createConnection(processID)\r
                if appName is None:\r
                        appName=getAppNameFromProcessID(processID)\r
                #: The application name.\r
                #: @type: str\r
                self.appName=appName\r
                self.processHandle=winKernel.openProcess(winKernel.SYNCHRONIZE,False,processID)\r
+               self.helperLocalBindingHandle=None\r
+               self._inprocRegistrationHandle=None\r
 \r
        def __repr__(self):\r
                return "<%r (appName %r, process ID %s) at address %x>"%(self.appModuleName,self.appName,self.processID,id(self))\r
@@ -219,7 +220,10 @@ class AppModule(baseObject.ScriptableObject):
                Subclasses should call the superclass method first.\r
                """\r
                winKernel.closeHandle(self.processHandle)\r
-               NVDAHelper.localLib.destroyConnection(self.helperLocalBindingHandle)\r
+               if self._inprocRegistrationHandle:\r
+                       ctypes.windll.rpcrt4.RpcSsDestroyClientContext(ctypes.byref(self._inprocRegistrationHandle))\r
+               if self.helperLocalBindingHandle:\r
+                       ctypes.windll.rpcrt4.RpcBindingFree(ctypes.byref(self.helperLocalBindingHandle))\r
 \r
        def chooseNVDAObjectOverlayClasses(self, obj, clsList):\r
                """Choose NVDAObject overlay classes for a given NVDAObject.\r
index 7c428ae..e271d42 100644 (file)
@@ -172,6 +172,9 @@ This initializes all modules such as audio, IAccessible, keyboard, mouse, and GU
        log.info("Using Python version %s"%sys.version)\r
        log.info("Using comtypes version %s"%comtypes.__version__)\r
        log.debug("Creating wx application instance")\r
+       import appModuleHandler\r
+       log.debug("Initializing appModule Handler")\r
+       appModuleHandler.initialize()\r
        import NVDAHelper\r
        log.debug("Initializing NVDAHelper")\r
        NVDAHelper.initialize()\r
@@ -226,9 +229,6 @@ This initializes all modules such as audio, IAccessible, keyboard, mouse, and GU
                locale.Init(lang,wxLang)\r
        except:\r
                pass\r
-       import appModuleHandler\r
-       log.debug("Initializing appModule Handler")\r
-       appModuleHandler.initialize()\r
        import api\r
        import winUser\r
        import NVDAObjects.window\r