From 7c0e25845a483312a475309024c3e0ae4b76af58 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 24 Aug 2017 15:40:14 +1000 Subject: [PATCH 1/9] Allow IAccessible2 to be used again in Firefox after a restart of NVDA on Windows 10 Creaters update. Also abstract code making it easy to register other COM proxy dlls. --- nvdaHelper/COMProxy.manifest.subst | 7 + nvdaHelper/ia2_sconscript | 36 ++++- nvdaHelper/remote/COMProxyRegistration.cpp | 156 +++++++++++++++++++++ nvdaHelper/remote/COMProxyRegistration.h | 42 ++++++ nvdaHelper/remote/IA2Support.cpp | 80 +++-------- nvdaHelper/remote/sconscript | 1 + 6 files changed, 259 insertions(+), 63 deletions(-) create mode 100644 nvdaHelper/COMProxy.manifest.subst create mode 100644 nvdaHelper/remote/COMProxyRegistration.cpp create mode 100644 nvdaHelper/remote/COMProxyRegistration.h diff --git a/nvdaHelper/COMProxy.manifest.subst b/nvdaHelper/COMProxy.manifest.subst new file mode 100644 index 00000000000..9a3c851a954 --- /dev/null +++ b/nvdaHelper/COMProxy.manifest.subst @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/nvdaHelper/ia2_sconscript b/nvdaHelper/ia2_sconscript index 75935a4469a..e9d25f0ba1e 100644 --- a/nvdaHelper/ia2_sconscript +++ b/nvdaHelper/ia2_sconscript @@ -14,16 +14,46 @@ Import('env') +proxyName="IAccessible2Proxy" +proxyClsid="{62d295fe-2062-4369-a010-4f59b5e32d5e}" +d=proxyClsid[1:-1].replace('-','') +proxyClsid_data="{%s,%s,%s,%s}"%( + "0x"+d[0:8], + "0x"+d[8:12], + "0x"+d[12:16], + "{%s}"%(",".join("0x"+d[x:x+2] for x in xrange(16,32,2))) +) + idlFile=env.Command("ia2.idl","#/miscDeps/include/ia2/ia2.idl",Copy("$TARGET","$SOURCE")) +manifestFile=env.Substfile( + target='ia2.manifest', + source='COMProxy.manifest.subst', + SUBST_DICT={ + '%proxyClsid%':proxyClsid, + '%proxyName%':proxyName, + } +) tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFile=env.TypeLibrary(source=idlFile) proxyDll=env.SharedLibrary( - target='IAccessible2Proxy', + target=proxyName, source=[iidSourceFile,proxySourceFile,dlldataSourceFile], LIBS=['rpcrt4','oleaut32','ole32'], - CPPDEFINES=[env['CPPDEFINES'],'WIN32','REGISTER_PROXY_DLL'], - LINKFLAGS=[env['LINKFLAGS'],'/export:DllGetClassObject,private','/export:DllCanUnloadNow,private'], + CPPDEFINES=[ + env['CPPDEFINES'], + 'WIN32', + ('PROXY_CLSID_IS',proxyClsid_data), + ], + LINKFLAGS=[ + env['LINKFLAGS'], + '/export:DllGetClassObject,private', + '/export:DllCanUnloadNow,private', + '/export:GetProxyDllInfo,private', + '/manifest:embed', + '/manifestinput:'+manifestFile[0].path, + ], ) +env.Depends(proxyDll,manifestFile) Return(['proxyDll','tlbFile','headerFile','iidSourceFile','proxySourceFile','dlldataSourceFile']) diff --git a/nvdaHelper/remote/COMProxyRegistration.cpp b/nvdaHelper/remote/COMProxyRegistration.cpp new file mode 100644 index 00000000000..3a720f466b2 --- /dev/null +++ b/nvdaHelper/remote/COMProxyRegistration.cpp @@ -0,0 +1,156 @@ +/* +This file is a part of the NVDA project. +URL: http://www.nvda-project.org/ +Copyright 2006-2010 NVDA contributers. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2.0, as published by + the Free Software Foundation. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +This license can be found at: +http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +*/ + +#include +#include +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#define CINTERFACE +#include +#include +#include +#include +#include "COMProxyRegistration.h" + +using namespace std; + +typedef void(RPC_ENTRY *LPFNGETPROXYDLLINFO)(ProxyFileInfo***, CLSID**); + +const wchar_t* StringCLSID_StandardMarshaler=L"{00020424-0000-0000-C000-000000000046}"; + +COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { + int res; + // Fetch the CLSID for the standard marshaler which will be used to unregister PS CLSIDs later + CLSID clsid_standardMarshaler; + if((res=IIDFromString(StringCLSID_StandardMarshaler,&clsid_standardMarshaler))!=S_OK) { + LOG_ERROR(L"Could not get clsid for standard marshaler"); + return nullptr; + } + // Generate a new unique CLSID to use for class object registration + CLSID regClsid={0}; + if((res=CoCreateGuid(®Clsid))!=S_OK) { + LOG_ERROR(L"Unable to generate registration CLSID"); + return nullptr; + } + // load the proxy dll + HMODULE dllHandle=LoadLibrary(dllPath); + if(dllHandle==NULL) { + LOG_ERROR(L"LoadLibrary failed for "<lpVtbl->Release(ClassObjPunk); + if(res!=S_OK) { + LOG_ERROR(L"Error registering class object for "<dllPath=dllPath; + reg->classObjectRegistrationCookie=dwCookie; + // For all interfaces the proxy dll supports, register its CLSID as their proxy stub CLSID + ProxyFileInfo** tempInfoPtr=pProxyInfo; + while(*tempInfoPtr) { + ProxyFileInfo& fileInfo=**tempInfoPtr; + for(unsigned short idx=0;idxheader.piid); + CLSID clsidBackup={0}; + wstring_convert> converter; + wstring name=converter.from_bytes(fileInfo.pNamesArray[idx]); + // Fetch the old CLSID for this interface if one is set, so we can replace it on deregistration. + // If not set, then we'll use the standard marshaler clsid on deregistration. + if((res=CoGetPSClsid(iid,&clsidBackup))!=S_OK) { + clsidBackup=clsid_standardMarshaler; + } + if((res=CoRegisterPSClsid(iid,regClsid))!=S_OK) { + LOG_ERROR(L"Unable to register interface "<psClsidBackups.push_back({name,iid,clsidBackup}); + } + ++tempInfoPtr; + } + // We can now safely free the proxy dll. COM will keep it loaded or re-load it if needed + FreeLibrary(dllHandle); + return reg; +} + +bool unregisterCOMProxy(COMProxyRegistration_t* reg) { + if(!reg) return false; + HRESULT res; + for(auto& backup: reg->psClsidBackups) { + if((res=CoRegisterPSClsid(backup.iid,backup.clsid))!=S_OK) { + LOG_ERROR(L"Error registering backup PSClsid for interface "<<(backup.name)<dllPath)<classObjectRegistrationCookie)))!=S_OK) { + LOG_ERROR(L"Error unregistering class object from "<<(reg->dllPath)< +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#include + +typedef struct { + std::wstring name; + IID iid; + CLSID clsid; +} PSClsidBackup_t; + +typedef struct { + std::wstring dllPath; + ULONG_PTR classObjectRegistrationCookie; + std::vector psClsidBackups; +} COMProxyRegistration_t; + +COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath); +bool unregisterCOMProxy(COMProxyRegistration_t* reg); + +#endif + diff --git a/nvdaHelper/remote/IA2Support.cpp b/nvdaHelper/remote/IA2Support.cpp index 597d8505cbb..b3cae4b6aa9 100755 --- a/nvdaHelper/remote/IA2Support.cpp +++ b/nvdaHelper/remote/IA2Support.cpp @@ -24,39 +24,22 @@ This license can be found at: #include "dllmain.h" #include "inProcess.h" #include "nvdaInProcUtils.h" +#include "COMProxyRegistration.h" #include "IA2Support.h" +using namespace std; + #define APPLICATION_USER_MODEL_ID_MAX_LENGTH 131 typedef LONG(WINAPI *GetCurrentApplicationUserModelId_funcType)(UINT32*,PWSTR); typedef ULONG(*LPFNDLLCANUNLOADNOW)(); #pragma data_seg(".ia2SupportShared") wchar_t IA2DllPath[MAX_PATH]={0}; -IID ia2Iids[]={ - IID_IAccessible2, - IID_IAccessibleAction, - IID_IAccessibleApplication, - IID_IAccessibleComponent, - IID_IAccessibleEditableText, - IID_IAccessibleHyperlink, - IID_IAccessibleHypertext, - IID_IAccessibleImage, - IID_IAccessibleRelation, - IID_IAccessibleTable, - IID_IAccessibleTable2, - IID_IAccessibleTableCell, - IID_IAccessibleText, - IID_IAccessibleValue, -}; #pragma data_seg() #pragma comment(linker, "/section:.ia2SupportShared,rws") -#define IAccessible2ProxyIID IID_IAccessible2 - -IID _ia2PSClsidBackups[ARRAYSIZE(ia2Iids)]={0}; bool isIA2Installed=FALSE; -HINSTANCE IA2DllHandle=0; -DWORD IA2RegCooky=0; +COMProxyRegistration_t* IA2ProxyRegistration; HANDLE IA2UIThreadHandle=NULL; DWORD IA2UIThreadID=0; HANDLE IA2UIThreadUninstalledEvent=NULL; @@ -65,54 +48,31 @@ bool isIA2Initialized=FALSE; bool isIA2SupportDisabled=false; bool installIA2Support() { - LPFNGETCLASSOBJECT IA2Dll_DllGetClassObject; - int i; - int res; if(isIA2Installed) return FALSE; - if((IA2DllHandle=CoLoadLibrary(IA2DllPath,FALSE))==NULL) { - LOG_ERROR(L"CoLoadLibrary failed"); - return FALSE; - } - IA2Dll_DllGetClassObject=(LPFNGETCLASSOBJECT)GetProcAddress(static_cast(IA2DllHandle),"DllGetClassObject"); - nhAssert(IA2Dll_DllGetClassObject); //IAccessible2 proxy dll must have this function - IUnknown* ia2ClassObjPunk=NULL; - if((res=IA2Dll_DllGetClassObject(IAccessible2ProxyIID,IID_IUnknown,(LPVOID*)&ia2ClassObjPunk))!=S_OK) { - LOG_ERROR(L"Error calling DllGetClassObject, code "<Release(); - CoFreeLibrary(IA2DllHandle); - IA2DllHandle=0; - return FALSE; + APTTYPE appType; + APTTYPEQUALIFIER aptQualifier; + HRESULT res; + if((res=CoGetApartmentType(&appType,&aptQualifier))!=S_OK) { + if(res!=CO_E_NOTINITIALIZED) { + LOG_ERROR(L"Error getting apartment type, code "<Release(); - for(i=0;i(IA2DllHandle),"DllCanUnloadNow"); - nhAssert(IA2Dll_DllCanUnloadNow); //IAccessible2 proxy dll must have this function - if(IA2Dll_DllCanUnloadNow()==S_OK) { - CoFreeLibrary(IA2DllHandle); + if(!isIA2Installed) return false; + if(!unregisterCOMProxy(IA2ProxyRegistration)) { + LOG_ERROR(L"Error unregistering IAccessible2 proxy"); + return false; } - IA2DllHandle=0; isIA2Installed=FALSE; return TRUE; } diff --git a/nvdaHelper/remote/sconscript b/nvdaHelper/remote/sconscript index b9a16b50164..f4c4b968d05 100644 --- a/nvdaHelper/remote/sconscript +++ b/nvdaHelper/remote/sconscript @@ -84,6 +84,7 @@ remoteLib=env.SharedLibrary( "typedCharacter.cpp", "ime.cpp", "tsf.cpp", + "COMProxyRegistration.cpp", "ia2Support.cpp", "ia2LiveRegions.cpp", ia2utilsObj, From 70a88f52a2bfbdb974e75fd584a8b8521db20ecf Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Tue, 29 Aug 2017 12:01:16 +1000 Subject: [PATCH 2/9] Build and register ISimpleDOM proxy ourselves allowing for math n Google chrome. --- nvdaHelper/ISimpleDOM_sconscript | 19 +++++++- nvdaHelper/archBuild_sconscript | 55 +++++++++++++++++++++- nvdaHelper/ia2_sconscript | 39 ++------------- nvdaHelper/remote/COMProxyRegistration.cpp | 13 ++++- nvdaHelper/remote/COMProxyRegistration.h | 19 ++++++++ nvdaHelper/remote/IA2Support.cpp | 32 ++++++------- nvdaHelper/remote/IA2Support.h | 1 - nvdaHelper/remote/injection.cpp | 5 -- 8 files changed, 121 insertions(+), 62 deletions(-) diff --git a/nvdaHelper/ISimpleDOM_sconscript b/nvdaHelper/ISimpleDOM_sconscript index 66b9feb43ec..2474b31d74f 100644 --- a/nvdaHelper/ISimpleDOM_sconscript +++ b/nvdaHelper/ISimpleDOM_sconscript @@ -16,9 +16,18 @@ Import('env') env['MIDLCOM']=env['MIDLCOM'][:-6] +# Copy some secondary IDL files included by ISimpleDOMNode.idl env.Command("ISimpleDOMText.idl","#/miscDeps/include/ISimpleDOM/ISimpleDOMText.idl",Copy("$TARGET","$SOURCE")) env.Command("ISimpleDOMDocument.idl","#/miscDeps/include/ISimpleDOM/ISimpleDOMDocument.idl",Copy("$TARGET","$SOURCE")) -idlFile=env.Command("ISimpleDOMNode.idl","#/miscDeps/include/ISimpleDOM/ISimpleDOMNode.idl",Copy("$TARGET","$SOURCE")) +# copy ISimpleDOMNode.idl but changing imports of the secondary files to #includes +# This is necessary as midl will not build secondary header files. this way the primary header file will contain all secondary header file content +idlFile=env.Substfile( + target="iSimpleDOMNode.idl", + source="#/miscDeps/include/ISimpleDOM/ISimpleDOMNode.idl", + SUBST_DICT={ + 'import "ISimpleDOM':'#include "ISimpleDOM', + } +) tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFile=env.TypeLibrary( source=idlFile, @@ -30,4 +39,10 @@ midl=env.WhereIs(env["MIDL"]) for target in (tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFile): env.Ignore(target,midl) -Return(['tlbFile','headerFile','iidSourceFile','proxySourceFile','dlldataSourceFile']) +proxyDll=env.COMProxyDll( + target='ISimpleDOM', + source=[iidSourceFile,proxySourceFile,dlldataSourceFile], + proxyClsid="{435E0FC9-344B-41D4-88DD-4CAAD499ACE5}", +) + +Return(['proxyDll','tlbFile','headerFile','iidSourceFile','proxySourceFile','dlldataSourceFile']) diff --git a/nvdaHelper/archBuild_sconscript b/nvdaHelper/archBuild_sconscript index 774aa279f6a..5148e1d3cda 100644 --- a/nvdaHelper/archBuild_sconscript +++ b/nvdaHelper/archBuild_sconscript @@ -23,6 +23,56 @@ Import( 'clientInstallDir', ) +# some utilities for COM proxies +def clsidStringToCLSIDDefine(clsidString): + """ + Converts a CLSID string of the form "{abcdef12-abcd-abcd-abcd-abcdef123456}" + Into a c-style struct initializer for initializing a GUID (I.e. "{0xabcdef12,0xabcd,0xabcd,{0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab}}") + """ + d=clsidString[1:-1].replace('-','') + return "{%s,%s,%s,%s}"%( + "0x"+d[0:8], + "0x"+d[8:12], + "0x"+d[12:16], + "{%s}"%(",".join("0x"+d[x:x+2] for x in xrange(16,32,2))) + ) + +def COMProxyDllBuilder(env,target,source,proxyClsid): + """ + Builds a COM proxy dll from iid, proxy and dlldata c files generated from an IDL file with MIDL. + It provides the needed linker flags, and also embeds a manifest in the dll registering the given proxy CLSID for this dll's class object. + """ + proxyName=str(target) + manifestFile=env.Substfile( + target=proxyName+'.manifest', + source='COMProxy.manifest.subst', + SUBST_DICT={ + '%proxyClsid%':proxyClsid, + '%proxyName%':proxyName, + } + ) + proxyDll=env.SharedLibrary( + target=target, + source=source, + LIBS=['rpcrt4','oleaut32','ole32'], + CPPDEFINES=[ + env['CPPDEFINES'], + 'WIN32', + ('PROXY_CLSID_IS',clsidStringToCLSIDDefine(proxyClsid)), + ], + LINKFLAGS=[ + env['LINKFLAGS'], + '/export:DllGetClassObject,private', + '/export:DllCanUnloadNow,private', + '/export:GetProxyDllInfo,private', + '/manifest:embed', + '/manifestinput:'+manifestFile[0].path, + ], + ) + env.Depends(proxyDll,manifestFile) + return proxyDll +env.AddMethod(COMProxyDllBuilder,'COMProxyDll') + # We only support compiling with MSVC 14 (2015) if not env.get('MSVC_VERSION','').startswith('14.'): raise RuntimeError("Microsoft Visual C++ 14 not found") @@ -89,8 +139,11 @@ if TARGET_ARCH=='x86': env.Install(sourceTypelibDir,ia2RPCStubs[1]) #typelib iSimpleDomRPCStubs=env.SConscript('ISimpleDOM_sconscript') +if signExec: + env.AddPostAction(iSimpleDomRPCStubs[0],[signExec]) +env.Install(libInstallDir,iSimpleDomRPCStubs[0]) #proxy dll if TARGET_ARCH=='x86': - env.Install(sourceTypelibDir,iSimpleDomRPCStubs[0]) #typelib + env.Install(sourceTypelibDir,iSimpleDomRPCStubs[1]) #typelib mathPlayerRPCStubs=env.SConscript('mathPlayer_sconscript') if TARGET_ARCH=='x86': diff --git a/nvdaHelper/ia2_sconscript b/nvdaHelper/ia2_sconscript index e9d25f0ba1e..130db3dfab9 100644 --- a/nvdaHelper/ia2_sconscript +++ b/nvdaHelper/ia2_sconscript @@ -14,46 +14,15 @@ Import('env') -proxyName="IAccessible2Proxy" -proxyClsid="{62d295fe-2062-4369-a010-4f59b5e32d5e}" -d=proxyClsid[1:-1].replace('-','') -proxyClsid_data="{%s,%s,%s,%s}"%( - "0x"+d[0:8], - "0x"+d[8:12], - "0x"+d[12:16], - "{%s}"%(",".join("0x"+d[x:x+2] for x in xrange(16,32,2))) -) - idlFile=env.Command("ia2.idl","#/miscDeps/include/ia2/ia2.idl",Copy("$TARGET","$SOURCE")) -manifestFile=env.Substfile( - target='ia2.manifest', - source='COMProxy.manifest.subst', - SUBST_DICT={ - '%proxyClsid%':proxyClsid, - '%proxyName%':proxyName, - } -) + tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFile=env.TypeLibrary(source=idlFile) -proxyDll=env.SharedLibrary( - target=proxyName, +proxyDll=env.COMProxyDll( + target='IAccessible2proxy', source=[iidSourceFile,proxySourceFile,dlldataSourceFile], - LIBS=['rpcrt4','oleaut32','ole32'], - CPPDEFINES=[ - env['CPPDEFINES'], - 'WIN32', - ('PROXY_CLSID_IS',proxyClsid_data), - ], - LINKFLAGS=[ - env['LINKFLAGS'], - '/export:DllGetClassObject,private', - '/export:DllCanUnloadNow,private', - '/export:GetProxyDllInfo,private', - '/manifest:embed', - '/manifestinput:'+manifestFile[0].path, - ], + proxyClsid="{62d295fe-2062-4369-a010-4f59b5e32d5e}" ) -env.Depends(proxyDll,manifestFile) Return(['proxyDll','tlbFile','headerFile','iidSourceFile','proxySourceFile','dlldataSourceFile']) diff --git a/nvdaHelper/remote/COMProxyRegistration.cpp b/nvdaHelper/remote/COMProxyRegistration.cpp index 3a720f466b2..977d3e78b3c 100644 --- a/nvdaHelper/remote/COMProxyRegistration.cpp +++ b/nvdaHelper/remote/COMProxyRegistration.cpp @@ -24,6 +24,7 @@ This license can be found at: #include #include #include +#include "dllmain.h" #include "COMProxyRegistration.h" using namespace std; @@ -33,6 +34,7 @@ typedef void(RPC_ENTRY *LPFNGETPROXYDLLINFO)(ProxyFileInfo***, CLSID**); const wchar_t* StringCLSID_StandardMarshaler=L"{00020424-0000-0000-C000-000000000046}"; COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { + LOG_DEBUG(L"Registering proxy "<psClsidBackups.push_back({name,iid,clsidBackup}); + LOG_DEBUG(L"Registered interface "<dllPath)); for(auto& backup: reg->psClsidBackups) { if((res=CoRegisterPSClsid(backup.iid,backup.clsid))!=S_OK) { LOG_ERROR(L"Error registering backup PSClsid for interface "<<(backup.name)<dllPath)<classObjectRegistrationCookie)))!=S_OK) { LOG_ERROR(L"Error unregistering class object from "<<(reg->dllPath)<dllPath)); delete reg; return true; } diff --git a/nvdaHelper/remote/COMProxyRegistration.h b/nvdaHelper/remote/COMProxyRegistration.h index 2908cab3a7b..ab3ebb7b14c 100644 --- a/nvdaHelper/remote/COMProxyRegistration.h +++ b/nvdaHelper/remote/COMProxyRegistration.h @@ -23,19 +23,38 @@ This license can be found at: #include #include +// Tracks information about a COM interface registered in-process typedef struct { + // The name of the interface (for debugging) std::wstring name; + // The unique identifier of the interface IID iid; + // The CLSID of the original class object that handled creating / proxying of this interface. + // Used when unregistering, so we can put things back the way they were CLSID clsid; } PSClsidBackup_t; +// Represents the registration of a COM proxy dll and its interfaces. +// this can be used for later unregistration of the COM proxy dll typedef struct { + // The path to the dll (for debugging) std::wstring dllPath; + // The cookie returned by CoRegisterClassObject, for later unregistration via CoRevokeClassObject ULONG_PTR classObjectRegistrationCookie; + // Information for all the interfaces registered for this proxy dll via CoRegisterPSClsid std::vector psClsidBackups; } COMProxyRegistration_t; +/* Registers a COM proxy dll and all its interfaces for this process so that they can be marshalled to/from other processes + @param dllPath the relative path to the proxy dll (relative from this dll) + @return registration data which can be later passed to UnregisterCOMProxy. + */ COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath); + +/* Unregisters a COM proxy dll originally registered with registerCOMProxy + @param reg the registration data returned by registerCOMProxy. + @return true if successful, false otherwise + */ bool unregisterCOMProxy(COMProxyRegistration_t* reg); #endif diff --git a/nvdaHelper/remote/IA2Support.cpp b/nvdaHelper/remote/IA2Support.cpp index b3cae4b6aa9..3e789fc76c7 100755 --- a/nvdaHelper/remote/IA2Support.cpp +++ b/nvdaHelper/remote/IA2Support.cpp @@ -33,13 +33,9 @@ using namespace std; typedef LONG(WINAPI *GetCurrentApplicationUserModelId_funcType)(UINT32*,PWSTR); typedef ULONG(*LPFNDLLCANUNLOADNOW)(); -#pragma data_seg(".ia2SupportShared") -wchar_t IA2DllPath[MAX_PATH]={0}; -#pragma data_seg() -#pragma comment(linker, "/section:.ia2SupportShared,rws") - bool isIA2Installed=FALSE; COMProxyRegistration_t* IA2ProxyRegistration; +COMProxyRegistration_t* ISimpleDOMProxyRegistration; HANDLE IA2UIThreadHandle=NULL; DWORD IA2UIThreadID=0; HANDLE IA2UIThreadUninstalledEvent=NULL; @@ -58,32 +54,34 @@ bool installIA2Support() { } return false; } - IA2ProxyRegistration=registerCOMProxy(IA2DllPath); + IA2ProxyRegistration=registerCOMProxy(L"IAccessible2Proxy.dll"); if(!IA2ProxyRegistration) { LOG_ERROR(L"Error registering IAccessible2 proxy"); - return false; + } + ISimpleDOMProxyRegistration=registerCOMProxy(L"ISimpleDOM.dll"); + if(!ISimpleDOMProxyRegistration) { + LOG_ERROR(L"Error registering ISimpleDOM proxy"); } isIA2Installed=TRUE; - return TRUE; + return isIA2Installed; } bool uninstallIA2Support() { if(!isIA2Installed) return false; - if(!unregisterCOMProxy(IA2ProxyRegistration)) { + if(ISimpleDOMProxyRegistration&&!unregisterCOMProxy(ISimpleDOMProxyRegistration)) { + LOG_ERROR(L"Error unregistering ISimpleDOM proxy"); + } else { + ISimpleDOMProxyRegistration=nullptr; + } + if(IA2ProxyRegistration&&!unregisterCOMProxy(IA2ProxyRegistration)) { LOG_ERROR(L"Error unregistering IAccessible2 proxy"); - return false; + } else { + IA2ProxyRegistration=nullptr; } isIA2Installed=FALSE; return TRUE; } -bool IA2Support_initialize() { - nhAssert(!isIA2Initialized); - wsprintf(IA2DllPath,L"%s\\IAccessible2Proxy.dll",dllDirectory); - isIA2Initialized=TRUE; - return TRUE; -} - void CALLBACK IA2Support_winEventProcHook(HWINEVENTHOOK hookID, DWORD eventID, HWND hwnd, long objectID, long childID, DWORD threadID, DWORD time) { if (eventID != EVENT_SYSTEM_FOREGROUND && eventID != EVENT_OBJECT_FOCUS) return; diff --git a/nvdaHelper/remote/IA2Support.h b/nvdaHelper/remote/IA2Support.h index 359e7056c91..3ddf17d573d 100755 --- a/nvdaHelper/remote/IA2Support.h +++ b/nvdaHelper/remote/IA2Support.h @@ -22,7 +22,6 @@ bool installIA2Support(); bool uninstallIA2Support(); //Private functions -bool IA2Support_initialize(); void IA2Support_inProcess_initialize(); void IA2Support_inProcess_terminate(); diff --git a/nvdaHelper/remote/injection.cpp b/nvdaHelper/remote/injection.cpp index 780ff25c987..e931d332156 100644 --- a/nvdaHelper/remote/injection.cpp +++ b/nvdaHelper/remote/injection.cpp @@ -21,7 +21,6 @@ This license can be found at: #include #include #include -#include "ia2Support.h" #include "apiHook.h" #include "nvdaController.h" #include "nvdaControllerInternal.h" @@ -321,10 +320,6 @@ BOOL injection_initialize(int secureMode) { return FALSE; } nhAssert(dllHandle); - if(!IA2Support_initialize()) { - MessageBox(NULL,L"Error initializing IA2 support",L"nvdaHelperRemote (injection_initialize)",0); - return FALSE; - } outprocMgrThreadHandle=CreateThread(NULL,0,outprocMgrThreadFunc,NULL,0,&outprocMgrThreadID); outprocInitialized=TRUE; return TRUE; From d6ee83abdfeb703b8099ac3c073da6048fb2a16a Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Tue, 29 Aug 2017 15:22:30 +1000 Subject: [PATCH 3/9] VirtualBuffer.prepare: do not do anything at all if the rootNVDAObject's appModule's helperLocalBindingHandle is missing. There may not be any binding handle yet if NVDA was started and the document in question already had focus. All though the virtualBuffer will still not yet work, at least the user can now move focus away and back again and prepare will be tried again once the binding handle is there. --- source/virtualBuffers/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/virtualBuffers/__init__.py b/source/virtualBuffers/__init__.py index 2a09fde035e..1532cf24e36 100644 --- a/source/virtualBuffers/__init__.py +++ b/source/virtualBuffers/__init__.py @@ -382,6 +382,12 @@ def __init__(self,rootNVDAObject,backendName=None): self.rootIdentifiers[self.rootDocHandle, self.rootID] = self def prepare(self): + if not self.rootNVDAObject.appModule.helperLocalBindingHandle: + # #5758: If NVDA starts with a document already in focus, there will have been no focus event to inject nvdaHelper yet. + # So at very least don't try to prepare a virtualBuffer as it will fail. + # The user will most likely need to manually move focus away and back again to allow this virtualBuffer to work. + log.debugWarning("appModule has no binding handle to injected code, can't prepare virtualBuffer yet.") + return self.shouldPrepare=False self.loadBuffer() From 730fd5f64c6bdd1e68e29c9217b073b09e109734 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 30 Aug 2017 10:18:51 +1000 Subject: [PATCH 4/9] Address review actions. --- nvdaHelper/ISimpleDOM_sconscript | 3 +- nvdaHelper/archBuild_sconscript | 2 +- nvdaHelper/ia2_sconscript | 4 +-- nvdaHelper/remote/COMProxyRegistration.cpp | 37 ++++++++++++++++++---- nvdaHelper/remote/COMProxyRegistration.h | 2 +- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/nvdaHelper/ISimpleDOM_sconscript b/nvdaHelper/ISimpleDOM_sconscript index 2474b31d74f..6457ae27577 100644 --- a/nvdaHelper/ISimpleDOM_sconscript +++ b/nvdaHelper/ISimpleDOM_sconscript @@ -1,7 +1,7 @@ ### #This file is a part of the NVDA project. #URL: http://www.nvda-project.org/ -#Copyright 2014-2017 NV Access Limited. +#Copyright (C) 2014-2017 NV Access Limited. #This program is free software: you can redistribute it and/or modify #it under the terms of the GNU General Public License version 2.0, as published by #the Free Software Foundation. @@ -42,6 +42,7 @@ for target in (tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFil proxyDll=env.COMProxyDll( target='ISimpleDOM', source=[iidSourceFile,proxySourceFile,dlldataSourceFile], + # This CLSID must be unique to this dll. A new one can be generated with import comtypes; comtypes.GUID.create_new() proxyClsid="{435E0FC9-344B-41D4-88DD-4CAAD499ACE5}", ) diff --git a/nvdaHelper/archBuild_sconscript b/nvdaHelper/archBuild_sconscript index 5148e1d3cda..b0f88d946cf 100644 --- a/nvdaHelper/archBuild_sconscript +++ b/nvdaHelper/archBuild_sconscript @@ -27,7 +27,7 @@ Import( def clsidStringToCLSIDDefine(clsidString): """ Converts a CLSID string of the form "{abcdef12-abcd-abcd-abcd-abcdef123456}" - Into a c-style struct initializer for initializing a GUID (I.e. "{0xabcdef12,0xabcd,0xabcd,{0xab,0xab,0xab,0xab,0xab,0xab,0xab,0xab}}") + Into a c-style struct initializer for initializing a GUID (I.e. "{0xabcdef12,0xabcd,0xabcd,{0xab,0xcd,0xab,0xcd,0xef,0x12,0x34,0x56}}") """ d=clsidString[1:-1].replace('-','') return "{%s,%s,%s,%s}"%( diff --git a/nvdaHelper/ia2_sconscript b/nvdaHelper/ia2_sconscript index 130db3dfab9..ab3c5cd8954 100644 --- a/nvdaHelper/ia2_sconscript +++ b/nvdaHelper/ia2_sconscript @@ -1,7 +1,7 @@ ### #This file is a part of the NVDA project. #URL: http://www.nvda-project.org/ -#Copyright 2006-2010 NVDA contributers. +#Copyright (C) 2006-2017 NV Access Limited. #This program is free software: you can redistribute it and/or modify #it under the terms of the GNU General Public License version 2.0, as published by #the Free Software Foundation. @@ -16,12 +16,12 @@ Import('env') idlFile=env.Command("ia2.idl","#/miscDeps/include/ia2/ia2.idl",Copy("$TARGET","$SOURCE")) - tlbFile,headerFile,iidSourceFile,proxySourceFile,dlldataSourceFile=env.TypeLibrary(source=idlFile) proxyDll=env.COMProxyDll( target='IAccessible2proxy', source=[iidSourceFile,proxySourceFile,dlldataSourceFile], + # This CLSID must be unique to this dll. A new one can be generated with import comtypes; comtypes.GUID.create_new() proxyClsid="{62d295fe-2062-4369-a010-4f59b5e32d5e}" ) diff --git a/nvdaHelper/remote/COMProxyRegistration.cpp b/nvdaHelper/remote/COMProxyRegistration.cpp index 977d3e78b3c..b1a6aced65b 100644 --- a/nvdaHelper/remote/COMProxyRegistration.cpp +++ b/nvdaHelper/remote/COMProxyRegistration.cpp @@ -1,7 +1,7 @@ /* This file is a part of the NVDA project. URL: http://www.nvda-project.org/ -Copyright 2006-2010 NVDA contributers. +Copyright (C) 2017 NV Access Limited. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.0, as published by the Free Software Foundation. @@ -31,6 +31,11 @@ using namespace std; typedef void(RPC_ENTRY *LPFNGETPROXYDLLINFO)(ProxyFileInfo***, CLSID**); +// The CLSID representing the Windows COM standard marshaller +// Many built-in COM interfaces in Windows point to this class object to handle marshalling, however there does not seem to be a constant for it in the windows SDK. +// Some non-Microsoft sources: +// http://www.mazecomputer.com/sxs/help/proxy.htm +// http://thrysoee.dk/InsideCOM+/ch12d.htm const wchar_t* StringCLSID_StandardMarshaler=L"{00020424-0000-0000-C000-000000000046}"; COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { @@ -38,13 +43,15 @@ COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { int res; // Fetch the CLSID for the standard marshaler which will be used to unregister PS CLSIDs later CLSID clsid_standardMarshaler; - if((res=IIDFromString(StringCLSID_StandardMarshaler,&clsid_standardMarshaler))!=S_OK) { + res=IIDFromString(StringCLSID_StandardMarshaler,&clsid_standardMarshaler); + if(res!=S_OK) { LOG_ERROR(L"Could not get clsid for standard marshaler"); return nullptr; } // Generate a new unique CLSID to use for class object registration CLSID regClsid={0}; - if((res=CoCreateGuid(®Clsid))!=S_OK) { + res=CoCreateGuid(®Clsid); + if(res!=S_OK) { LOG_ERROR(L"Unable to generate registration CLSID"); return nullptr; } @@ -77,6 +84,8 @@ COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { ACTCTX actCtx={0}; actCtx.cbSize=sizeof(actCtx); actCtx.dwFlags=ACTCTX_FLAG_HMODULE_VALID|ACTCTX_FLAG_RESOURCE_NAME_VALID; + // The resource ID for a dll must be 2. + // See the linker's /manifest argument stating where the manifest is placed in a dll: https://docs.microsoft.com/en-gb/cpp/build/reference/manifest-create-side-by-side-assembly-manifest actCtx.lpResourceName=MAKEINTRESOURCE(2); actCtx.hModule=dllHandle; HANDLE hActCtx=CreateActCtx(&actCtx); @@ -117,6 +126,14 @@ COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { reg->dllPath=dllPath; reg->classObjectRegistrationCookie=dwCookie; // For all interfaces the proxy dll supports, register its CLSID as their proxy stub CLSID + // pProxyInfo is a pointer to a list of ProxyFileInfo pointers. The last of them being NULL to denote the end of the list. + // There is no official documentation on this, but + // in dlldata.c generated by MIDL (E.g. for IAccessible2, ia2_data.c), you can see: + // PROXYFILE_LIST_START, followed by REFERENCE_PROXY_FILE(IA2), followed by PROXYFILE_LIST_END. + // In RPCProxy.h from the Windows SDK, PROXYFILE_LIST_START declairs an unsized array of ProxyFileInfo pointers, REFERENCE_PROXY_FILE fills in each ProxyFileInfo pointer, and PROXYFILE_LIST_END places a final 0 to terminate the list. + // The reason it is a list is that multiple IDLs may be compiled into one proxy, and each IDL file gets its own ProxyFileInfo and therefore its own call to REFERENCE_PROXY_FILE + // Also see a similar implementation in Mozilla Gecko: + // https://hg.mozilla.org/mozilla-central/raw-file/1b4c59eef820b46eb0037aca68f83a15088db45f/ipc/mscom/Registration.cpp ProxyFileInfo** tempInfoPtr=pProxyInfo; while(*tempInfoPtr) { ProxyFileInfo& fileInfo=**tempInfoPtr; @@ -127,12 +144,14 @@ COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { wstring name=converter.from_bytes(fileInfo.pNamesArray[idx]); // Fetch the old CLSID for this interface if one is set, so we can replace it on deregistration. // If not set, then we'll use the standard marshaler clsid on deregistration. - if((res=CoGetPSClsid(iid,&clsidBackup))!=S_OK) { + res=CoGetPSClsid(iid,&clsidBackup); + if(res!=S_OK) { clsidBackup=clsid_standardMarshaler; } else { LOG_DEBUG(L"Backed up existing clsid for interface "<dllPath)); for(auto& backup: reg->psClsidBackups) { - if((res=CoRegisterPSClsid(backup.iid,backup.clsid))!=S_OK) { + res=CoRegisterPSClsid(backup.iid,backup.clsid); + if(res!=S_OK) { LOG_ERROR(L"Error registering backup PSClsid for interface "<<(backup.name)<dllPath)<classObjectRegistrationCookie)))!=S_OK) { + res=CoRevokeClassObject((DWORD)(reg->classObjectRegistrationCookie)); + if(res!=S_OK) { LOG_ERROR(L"Error unregistering class object from "<<(reg->dllPath)<dllPath)); delete reg; diff --git a/nvdaHelper/remote/COMProxyRegistration.h b/nvdaHelper/remote/COMProxyRegistration.h index ab3ebb7b14c..92541336ca1 100644 --- a/nvdaHelper/remote/COMProxyRegistration.h +++ b/nvdaHelper/remote/COMProxyRegistration.h @@ -1,7 +1,7 @@ /* This file is a part of the NVDA project. URL: http://www.nvda-project.org/ -Copyright 2006-2010 NVDA contributers. +Copyright (C) 2017 NV Access Limited. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.0, as published by the Free Software Foundation. From fe71f7c2aec6197729a169c4aca40f84c2b358fb Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 30 Aug 2017 11:57:24 +1000 Subject: [PATCH 5/9] NVDAHelperRemote's unregisterCOMProxy: don't return early so we clean up as much as possible. --- nvdaHelper/remote/COMProxyRegistration.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/nvdaHelper/remote/COMProxyRegistration.cpp b/nvdaHelper/remote/COMProxyRegistration.cpp index b1a6aced65b..d12f44602e4 100644 --- a/nvdaHelper/remote/COMProxyRegistration.cpp +++ b/nvdaHelper/remote/COMProxyRegistration.cpp @@ -174,17 +174,14 @@ bool unregisterCOMProxy(COMProxyRegistration_t* reg) { res=CoRegisterPSClsid(backup.iid,backup.clsid); if(res!=S_OK) { LOG_ERROR(L"Error registering backup PSClsid for interface "<<(backup.name)<dllPath)<classObjectRegistrationCookie)); if(res!=S_OK) { LOG_ERROR(L"Error unregistering class object from "<<(reg->dllPath)<dllPath)); delete reg; return true; } - From 2aed84c02cb432ec432c6ac69ffd750d335b3c2b Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 31 Aug 2017 08:11:37 +1000 Subject: [PATCH 6/9] Remove need for CoGetApartmentType as it is not supported on XP. --- nvdaHelper/remote/COMProxyRegistration.cpp | 6 +++++- nvdaHelper/remote/IA2Support.cpp | 19 ++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/nvdaHelper/remote/COMProxyRegistration.cpp b/nvdaHelper/remote/COMProxyRegistration.cpp index d12f44602e4..d235f1059dd 100644 --- a/nvdaHelper/remote/COMProxyRegistration.cpp +++ b/nvdaHelper/remote/COMProxyRegistration.cpp @@ -108,7 +108,11 @@ COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { DeactivateActCtx(0,actCtxCookie); ReleaseActCtx(hActCtx); if(res!=S_OK) { - LOG_ERROR(L"Error fetching class object for "< Date: Thu, 31 Aug 2017 15:09:21 +1000 Subject: [PATCH 7/9] Revert "Remove need for CoGetApartmentType as it is not supported on XP." We are now planning to drop support for XP/Vista in PR #7546. This reverts commit 2aed84c02cb432ec432c6ac69ffd750d335b3c2b. --- nvdaHelper/remote/COMProxyRegistration.cpp | 6 +----- nvdaHelper/remote/IA2Support.cpp | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/nvdaHelper/remote/COMProxyRegistration.cpp b/nvdaHelper/remote/COMProxyRegistration.cpp index d235f1059dd..d12f44602e4 100644 --- a/nvdaHelper/remote/COMProxyRegistration.cpp +++ b/nvdaHelper/remote/COMProxyRegistration.cpp @@ -108,11 +108,7 @@ COMProxyRegistration_t* registerCOMProxy(wchar_t* dllPath) { DeactivateActCtx(0,actCtxCookie); ReleaseActCtx(hActCtx); if(res!=S_OK) { - if(res==CO_E_NOTINITIALIZED) { - LOG_DEBUGWARNING(L"Could not fetch class object as COM is not yet initialized."); - } else { - LOG_ERROR(L"Error fetching class object for "< Date: Sat, 2 Sep 2017 12:19:22 +1000 Subject: [PATCH 8/9] Make sure to unhook the inproc winEvent on normal termination! --- nvdaHelper/remote/injection.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nvdaHelper/remote/injection.cpp b/nvdaHelper/remote/injection.cpp index 780ff25c987..3bb7aaa3de2 100644 --- a/nvdaHelper/remote/injection.cpp +++ b/nvdaHelper/remote/injection.cpp @@ -214,6 +214,9 @@ if(isSecureModeNVDAProcess) real_OpenClipboard=apiHook_hookFunction_safe("USER32 inProcess_terminate(); //Unregister any windows hooks registered so far killRunningWindowsHooks(); + // Unregister inproc winEvent callback + UnhookWinEvent(inprocWinEventHookID); + inprocWinEventHookID=0; //Release and close the thread mutex ReleaseMutex(threadMutex); CloseHandle(threadMutex); From 952dfe3f3b0cd732b69571b25d412447d616978e Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Mon, 4 Sep 2017 13:00:54 +1000 Subject: [PATCH 9/9] Place all NVDA dlls in a version-specific lib directory to ensure that Windows will always load the correct dll when hooking, rather than possibly falling back to a dll in a previous version that may currently still be loaded in a process. --- appveyor.yml | 4 ++-- appveyor/mozillaSyms.py | 7 +++++-- sconstruct | 6 +++--- source/NVDAHelper.py | 12 ++++++++---- source/installer.py | 8 ++++++++ source/nvda_slave.pyw | 12 +++++++++++- source/setup.py | 4 ++-- 7 files changed, 39 insertions(+), 14 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 1bdbc9f68c9..888ba8ed091 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -85,9 +85,9 @@ build_script: foreach ($syms in # We don't just include source\*.dll because that would include system dlls. "source\liblouis.dll", "source\*.pdb", - "source\lib\*.dll", "source\lib\*.pdb", + "source\lib\$env:version\*.dll", "source\lib\$env:version\*.pdb", # We include source\lib64\*.exe to cover nvdaHelperRemoteLoader. - "source\lib64\*.dll", "source\lib64\*.exe", "source\lib64\*.pdb", + "source\lib64\$env:version\*.dll", "source\lib64\$env:version\*.exe", "source\lib64\$env:version\*.pdb", "source\synthDrivers\*.dll", "source\synthDrivers\*.pdb" ) { & $env:symstore add /s symbols /compress -:NOREFS /t NVDA /f $syms diff --git a/appveyor/mozillaSyms.py b/appveyor/mozillaSyms.py index 146741cfe6c..5db54c225a0 100644 --- a/appveyor/mozillaSyms.py +++ b/appveyor/mozillaSyms.py @@ -14,11 +14,12 @@ import zipfile import requests +NVDA_VERSION=os.getenv('version') SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) DUMP_SYMS = os.path.join(SCRIPT_DIR, "dump_syms.exe") NVDA_SOURCE = os.path.join(os.path.dirname(SCRIPT_DIR), "source") -NVDA_LIB = os.path.join(NVDA_SOURCE, "lib") -NVDA_LIB64 = NVDA_LIB + "64" +NVDA_LIB = os.path.join(NVDA_SOURCE, "lib",NVDA_VERSION) +NVDA_LIB64 = os.path.join(NVDA_SOURCE, "lib64",NVDA_VERSION) ZIP_FILE = os.path.join(SCRIPT_DIR, "mozillaSyms.zip") URL = 'https://crash-stats.mozilla.com/symbols/upload' @@ -26,6 +27,7 @@ # This only needs to include dlls injected into Mozilla products. DLL_NAMES = [ "IAccessible2Proxy.dll", + "ISimpleDOM.dll", "minHook.dll", "nvdaHelperRemote.dll", "VBufBackend_adobeFlash.dll", @@ -51,6 +53,7 @@ def check_output(command): return stdout def processFile(path): + print("dump_syms %s"%path) try: stdout = check_output([DUMP_SYMS, path]) except ProcError as e: diff --git a/sconstruct b/sconstruct index e34c62e579e..150ea9fe199 100755 --- a/sconstruct +++ b/sconstruct @@ -106,15 +106,15 @@ certFile = env["certFile"] certPassword = env["certPassword"] certTimestampServer = env["certTimestampServer"] userDocsDir=Dir('user_docs') -sourceDir = Dir("source") +sourceDir = env.Dir("source") Export('sourceDir') clientDir=Dir('extras/controllerClient') Export('clientDir') -sourceLibDir=sourceDir.Dir('lib') +sourceLibDir=sourceDir.Dir('lib').Dir(version) Export('sourceLibDir') sourceTypelibDir=sourceDir.Dir('typelibs') Export('sourceTypelibDir') -sourceLibDir64=sourceDir.Dir('lib64') +sourceLibDir64=sourceDir.Dir('lib64').Dir(version) Export('sourceLibDir64') buildDir = Dir("build") outFilePrefix = "nvda{type}_{version}".format(type="" if release else "_snapshot", version=version) diff --git a/source/NVDAHelper.py b/source/NVDAHelper.py index 53e19bd9207..280183a4370 100755 --- a/source/NVDAHelper.py +++ b/source/NVDAHelper.py @@ -2,6 +2,7 @@ import sys import _winreg import msvcrt +import versionInfo import winKernel import config @@ -17,6 +18,9 @@ import time import globalVars +versionedLibPath=os.path.join('lib',versionInfo.version) +versionedLib64Path=os.path.join('lib64',versionInfo.version) + _remoteLib=None _remoteLoader64=None localLib=None @@ -393,7 +397,7 @@ def __init__(self): # Therefore, explicitly specify our own process token, which causes them to be inherited. token = winKernel.OpenProcessToken(winKernel.GetCurrentProcess(), winKernel.MAXIMUM_ALLOWED) try: - winKernel.CreateProcessAsUser(token, None, u"lib64/nvdaHelperRemoteLoader.exe", None, None, True, None, None, None, si, pi) + winKernel.CreateProcessAsUser(token, None, os.path.join(versionedLib64Path,u"nvdaHelperRemoteLoader.exe"), None, None, True, None, None, None, si, pi) # We don't need the thread handle. winKernel.closeHandle(pi.hThread) self._process = pi.hProcess @@ -417,7 +421,7 @@ def terminate(self): def initialize(): global _remoteLib, _remoteLoader64, localLib, generateBeep,VBuf_getTextInRange - localLib=cdll.LoadLibrary('lib/nvdaHelperLocal.dll') + localLib=cdll.LoadLibrary(os.path.join(versionedLibPath,'nvdaHelperLocal.dll')) for name,func in [ ("nvdaController_speakText",nvdaController_speakText), ("nvdaController_cancelSpeech",nvdaController_cancelSpeech), @@ -449,7 +453,7 @@ def initialize(): ("VBuf_getTextInRange", localLib), ((1,), (1,), (1,), (2,), (1,))) #Load nvdaHelperRemote.dll but with an altered search path so it can pick up other dlls in lib - h=windll.kernel32.LoadLibraryExW(os.path.abspath(ur"lib\nvdaHelperRemote.dll"),0,0x8) + h=windll.kernel32.LoadLibraryExW(os.path.abspath(os.path.join(versionedLibPath,u"nvdaHelperRemote.dll")),0,0x8) if not h: log.critical("Error loading nvdaHelperRemote.dll: %s" % WinError()) return @@ -478,7 +482,7 @@ def terminate(): localLib.nvdaHelperLocal_terminate() localLib=None -LOCAL_WIN10_DLL_PATH = ur"lib\nvdaHelperLocalWin10.dll" +LOCAL_WIN10_DLL_PATH = os.path.join(versionedLibPath,"nvdaHelperLocalWin10.dll") def getHelperLocalWin10Dll(): """Get a ctypes WinDLL instance for the nvdaHelperLocalWin10 dll. This is a C++/CX dll used to provide access to certain UWP functionality. diff --git a/source/installer.py b/source/installer.py index bdc6d55954c..42fc16acd09 100644 --- a/source/installer.py +++ b/source/installer.py @@ -154,6 +154,14 @@ def removeOldProgramFiles(destPath): else: os.remove(fn) + # #7546: Remove old version-specific libs + for topDir in ('lib','lib64'): + for parent,subdirs,files in os.walk(os.path.join(destPath,topDir),topdown=False): + for d in subdirs: + tryRemoveFile(os.path.join(parent,d),numRetries=1,rebootOK=True) + for f in files: + tryRemoveFile(os.path.join(parent,f),numRetries=1,rebootOK=True) + # #4235: mpr.dll is a Windows system dll accidentally included with # earlier versions of NVDA. Its presence causes problems in Windows Vista. fn = os.path.join(destPath, "mpr.dll") diff --git a/source/nvda_slave.pyw b/source/nvda_slave.pyw index c7810c24c1c..a086f450b85 100755 --- a/source/nvda_slave.pyw +++ b/source/nvda_slave.pyw @@ -2,10 +2,20 @@ Performs miscellaneous tasks which need to be performed in a separate process. """ +import gettext +import locale +#Localization settings +locale.setlocale(locale.LC_ALL,'') +try: + gettext.translation('nvda',localedir='locale',languages=[locale.getlocale()[0]]).install(True) +except: + gettext.install('nvda',unicode=True) + import pythonMonkeyPatches import sys import os +import versionInfo import logHandler if hasattr(sys, "frozen"): # Error messages (which are only for debugging) should not cause the py2exe log message box to appear. @@ -73,7 +83,7 @@ def main(): raise ValueError("Addon path was not provided.") #Load nvdaHelperRemote.dll but with an altered search path so it can pick up other dlls in lib import ctypes - h=ctypes.windll.kernel32.LoadLibraryExW(os.path.abspath(ur"lib\nvdaHelperRemote.dll"),0,0x8) + h=ctypes.windll.kernel32.LoadLibraryExW(os.path.abspath(os.path.join(u"lib",versionInfo.version,u"nvdaHelperRemote.dll")),0,0x8) remoteLib=ctypes.WinDLL("nvdaHelperRemote",handle=h) ret = remoteLib.nvdaControllerInternal_installAddonPackageFromPath(addonPath) if ret != 0: diff --git a/source/setup.py b/source/setup.py index ecc4820e38f..4b836f27c92 100755 --- a/source/setup.py +++ b/source/setup.py @@ -222,8 +222,8 @@ def getRecursiveDataFiles(dest,source,excludes=()): data_files=[ (".",glob("*.dll")+glob("*.manifest")+["builtin.dic"]), ("documentation", ['../copying.txt', '../contributors.txt']), - ("lib", glob("lib/*.dll")), - ("lib64", glob("lib64/*.dll") + glob("lib64/*.exe")), + ("lib/%s"%version, glob("lib/%s/*.dll"%version)), + ("lib64/%s"%version, glob("lib64/%s/*.dll"%version) + glob("lib64/%s/*.exe"%version)), ("waves", glob("waves/*.wav")), ("images", glob("images/*.ico")), ("louis/tables",glob("louis/tables/*")),