Skip to content

Commit

Permalink
Repaired the COM Registration Fixing Tool so it is more effective on …
Browse files Browse the repository at this point in the history
…64bit Windows (nvaccess#9039).

Problems:
- The registerServer function called the 32-bit version of regsvr32.exe, even in 64-bit contexts.
- The applyRegistryPatch function called the 32-bit version of regedit, even in 64-bit contexts.
Remediation:
- Replaced applyRegistryPatch function with two new functions: apply32bitRegistryPatch and apply64bitRegistryPatch.
- Replaced registerServer function with register32bitServer and register64bitServer, to make clear what they do.
- The new functions don't check 32/64 bitness; they leave that to the caller and log errors on failure.
- Updated to more descriptive error logging.
- Adjusted the Windows 7 code to use server registration with proper bitness for each DLL.
Path remediations:
- Moved the OLEACC_REG_FILE_PATH constant to the top of the file with the rest.
- Added sysnative path to the list of path constants at the top of the file.
- Now use Sysnative in the path for intentional 64bit calls, per discussions linked from nvaccess#9039.
- Now use System32 in the path for 32-bit calls on either 32-bit or 64-bit systems.
- For convenience, and because it's required in System32, renamed regedit.exe to regedt32.exe in the subprocess calls.
- Removed now unused sysWow64 path constant.
Misc:
- Added docstring note about 32 and 64 bit functions needing attention if NVDA goes 64-bit in the future.
- Updated copyright.
  • Loading branch information
XLTechie committed Jun 21, 2021
1 parent 0d98162 commit b835942
Showing 1 changed file with 77 additions and 38 deletions.
115 changes: 77 additions & 38 deletions source/COMRegistrationFixes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2018-2020 NV Access Limited
# Copyright (C) 2018-2021 NV Access Limited
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

Expand All @@ -11,71 +11,110 @@
import globalVars
from logHandler import log

OLEACC_REG_FILE_PATH = os.path.join(globalVars.appDir, "COMRegistrationFixes", "oleaccProxy.reg")
# Particular 64 bit / 32 bit system paths
systemRoot=os.path.expandvars('%SYSTEMROOT%')
system32=os.path.join(systemRoot,'system32')
sysWow64=os.path.join(systemRoot,'syswow64')
systemDrive=os.path.expandvars('%SYSTEMDRIVE%\\')
programFiles=os.path.join(systemDrive,'program files')
programFilesX86=os.path.join(systemDrive,'program files (x86)')
systemRoot = os.path.expandvars("%SYSTEMROOT%")
system32 = os.path.join(systemRoot, "System32")
sysnative = os.path.join(systemRoot, "Sysnative") # Virtual folder for reaching 64-bit exes from 32-bit apps
systemDrive = os.path.expandvars("%SYSTEMDRIVE%\\")
programFiles = os.path.join(systemDrive, "program files")
programFilesX86 = os.path.join(systemDrive, "program files (x86)")


def registerServer(fileName,wow64=False):
def register32bitServer(fileName: str) -> None:
"""Registers the COM proxy dll with the given file name, using the 32-bit version of regsvr32.
Note: this function is valid while NVDA remains a 32-bit app. Re-evaluate if we move to 64-bit.
@param fileName: the path to the dll
@type fileName: str
"""
Registers the COM proxy dll with the given file name
Using regsvr32.
# Points to the 32-bit version, on Windows 32-bit or 64-bit.
regsvr32 = os.path.join(system32, "regsvr32.exe")
try:
subprocess.check_call([regsvr32, "/s", fileName])
except subprocess.CalledProcessError as e:
log.error(f"Error registering {fileName} in a 32-bit context: {e}")
else:
log.debug(f"Registered {fileName} in a 32-bit context.")


def register64bitServer(fileName: str) -> None:
"""Registers the COM proxy dll with the given file name, using the 64-bit version of regsvr64.
Note: this function is valid while NVDA remains a 32-bit app. Re-evaluate if we move to 64-bit.
@param fileName: the path to the dll
@type fileName: str
@param wow64: If true then the 32 bit (wow64) version of regsvr32 will be used.
@type wow64: bool
"""
regsvr32=os.path.join(sysWow64 if wow64 else system32,'regsvr32.exe')
# SysWOW64 provides a virtual directory to allow 32-bit programs to reach 64-bit executables.
regsvr32 = os.path.join(sysnative, "regsvr32.exe")
try:
subprocess.check_call([regsvr32,'/s',fileName])
subprocess.check_call([regsvr32, "/s", fileName])
except subprocess.CalledProcessError as e:
log.error("Error registering %s, %s"%(fileName,e))
log.error(f"Error registering {fileName} in a 64-bit context: {e}")
else:
log.debug("Registered %s"%fileName)
log.debug(f"Registered {fileName} in a 64-bit context.")


def applyRegistryPatch(fileName,wow64=False):
def apply32bitRegistryPatch(fileName: str) -> None:
"""Applies the registry patch with the given file name, using 32-bit regedit.
Note: this function is valid while NVDA remains a 32-bit app. Re-evaluate if we move to 64-bit.
@param fileName: the path to the .reg file
@type fileName: str
"""
Applies the registry patch with the given file name
using regedit.
if not os.path.isfile(fileName):
raise FileNotFoundError(f"Cannot apply 32-bit registry patch: {fileName} not found.")
# On 32-bit systems, regedt32 is in System32. On 64-bit systems, SysWOW64 will redirect to 32-bit version.
regedit = os.path.join(systemRoot, "System32", "regedt32.exe")
try:
subprocess.check_call([regedit, "/s", fileName])
except subprocess.CalledProcessError as e:
log.error(f"Error applying 32-bit registry patch from {fileName} with {regedit}: {e}")
else:
log.debug(f"Applied 32-bit registry patch from {fileName}")


def apply64bitRegistryPatch(fileName: str) -> None:
"""Applies the registry patch with the given file name, using 64-bit regedit.
Note: this function is valid while NVDA remains a 32-bit app. Re-evaluate if we move to 64-bit.
@param fileName: the path to the .reg file
@type fileName: str
"""
if not os.path.isfile(fileName):
raise FileNotFoundError(f"Cannot apply registry patch, {fileName} not found.")
regedit=os.path.join(sysWow64 if wow64 else systemRoot,'regedit.exe')
raise FileNotFoundError(f"Cannot apply 64-bit registry patch: {fileName} not found.")
# On 64-bit systems, SysWOW64 provides 32-bit apps with a virtual directory to reach 64-bit executables.
# For hard coded historical compatibility, regedt32.exe is still the name of the 64-bit app.
regedit = os.path.join(sysnative, "regedt32.exe")
try:
subprocess.check_call([regedit,'/s',fileName])
subprocess.check_call([regedit, "/s", fileName])
except subprocess.CalledProcessError as e:
log.error("Error applying registry patch: %s with %s, %s"%(fileName,regedit,e))
log.error(f"Error applying 64-bit registry patch from {fileName} with {regedit}: {e}")
else:
log.debug("Applied registry patch: %s with %s"%(fileName,regedit))
log.debug(f"Applied 64-bit registry patch from {fileName}")


OLEACC_REG_FILE_PATH = os.path.join(globalVars.appDir, "COMRegistrationFixes", "oleaccProxy.reg")
def fixCOMRegistrations():
"""Registers most common COM proxies, in case they have accidentally been unregistered or overwritten by
3rd party software installs/uninstalls.
"""
Registers most common COM proxies, in case they had accidentally been unregistered or overwritten by 3rd party software installs/uninstalls.
"""
is64bit=os.environ.get("PROCESSOR_ARCHITEW6432","").endswith('64')
is64bit = os.environ.get("PROCESSOR_ARCHITEW6432", "").endswith("64")
winVer = winVersion.getWinVer()
OSMajorMinor = (winVer.major, winVer.minor)
log.debug("Fixing COM registration for Windows %s.%s, %s"%(OSMajorMinor[0],OSMajorMinor[1],"64 bit" if is64bit else "32 bit"))
log.debug(
f"Fixing COM registration for Windows {OSMajorMinor[0]}.{OSMajorMinor[1]}, "
"{} bit.".format("64" if is64bit else "32")
)
# Commands taken from NVDA issue #2807 comment https://github.com/nvaccess/nvda/issues/2807#issuecomment-320149243
# And also the discussions in #9039 https://github.com/nvaccess/nvda/issues/9039
# OLEACC (MSAA) proxies
applyRegistryPatch(OLEACC_REG_FILE_PATH)
apply32bitRegistryPatch(OLEACC_REG_FILE_PATH)
if is64bit:
applyRegistryPatch(OLEACC_REG_FILE_PATH, wow64=True)
apply64bitRegistryPatch(OLEACC_REG_FILE_PATH)
# IDispatch and other common OLE interfaces
registerServer(os.path.join(system32,'oleaut32.dll'))
registerServer(os.path.join(system32,'actxprxy.dll'))
register32bitServer(os.path.join(system32, "oleaut32.dll"))
register32bitServer(os.path.join(system32, "actxprxy.dll"))
if is64bit:
registerServer(os.path.join(sysWow64,'oleaut32.dll'),wow64=True)
registerServer(os.path.join(sysWow64,'actxprxy.dll'),wow64=True)
register64bitServer(os.path.join(sysnative, "oleaut32.dll"))
register64bitServer(os.path.join(sysnative, "actxprxy.dll"))
# IServiceProvider on windows 7 can become unregistered
if OSMajorMinor==(6,1): # Windows 7
registerServer(os.path.join(programFiles,'Internet Explorer','ieproxy.dll'))
if OSMajorMinor == (6, 1): # Windows 7
register32bitServer(os.path.join(programFilesX86, "Internet Explorer", "ieproxy.dll"))
if is64bit:
registerServer(os.path.join(programFilesX86,'Internet Explorer','ieproxy.dll'),wow64=True)
register64bitServer(os.path.join(programFiles, "Internet Explorer", "ieproxy.dll"))

0 comments on commit b835942

Please sign in to comment.