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 (Partial fix of 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.
- The Win7 32-bit run did not take into account 32-bit only systems (no Program Files (x86) folder).
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.
- Now use System32 in the path for 32-bit calls on either 32-bit or 64-bit systems.
- Now use reg.exe's import option to load .reg files instead of regedit.exe.
- Now check whether to use "Program Files" or "Program Files (x86)" on Win7.
- 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.
- Converted path constants to uppercase-with-underscore style, and corrected case on some Windows paths.
- Moved comments with discussion links into module docstring, and rearranged.
- Updated copyright.
- In gui/__init__.py: added a recommendation that the user restart the computer, to the completion message.
  • Loading branch information
XLTechie committed Jun 25, 2021
1 parent 161ad1f commit f156f38
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 43 deletions.
125 changes: 85 additions & 40 deletions source/COMRegistrationFixes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,126 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2018-2020 NV Access Limited
# Copyright (C) 2018-2021 NV Access Limited, Luke Davis (Open Source Systems, Ltd.)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

"""Utilities to re-register particular system COM interfaces needed by NVDA."""
"""Utilities to re-register particular system COM interfaces needed by NVDA.
Relevant discussions of DLLs, registry keys, and paths, can be found on these issues:
https://github.com/nvaccess/nvda/issues/2807#issuecomment-320149243
https://github.com/nvaccess/nvda/issues/9039
https://github.com/nvaccess/nvda/issues/12560
"""

import os
import subprocess
import winVersion
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)')
SYSTEM_ROOT = os.path.expandvars("%SYSTEMROOT%")
SYSTEM32 = os.path.join(SYSTEM_ROOT, "System32")
SYSNATIVE = os.path.join(SYSTEM_ROOT, "Sysnative") # Virtual folder for reaching 64-bit exes from 32-bit apps
SYSTEM_DRIVE = os.path.expandvars("%SYSTEMDRIVE%\\")
PROGRAM_FILES = os.path.join(SYSTEM_DRIVE, "Program Files")
PROGRAM_FILES_X86 = os.path.join(SYSTEM_DRIVE, "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 regExe.
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, reg.exe is in System32. On 64-bit systems, SysWOW64 will redirect to 32-bit version.
regExe = os.path.join(SYSTEM_ROOT, "System32", "reg.exe")
try:
subprocess.check_call([regExe, "import", fileName])
except subprocess.CalledProcessError as e:
log.error(f"Error applying 32-bit registry patch from {fileName} with {regExe}: {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 regExe.
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.
regExe = os.path.join(SYSNATIVE, "reg.exe")
try:
subprocess.check_call([regedit,'/s',fileName])
subprocess.check_call([regExe, "import", 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 {regExe}: {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 or 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"))
# Commands taken from NVDA issue #2807 comment https://github.com/nvaccess/nvda/issues/2807#issuecomment-320149243
log.debug(
f"Fixing COM registrations for Windows {OSMajorMinor[0]}.{OSMajorMinor[1]}, "
"{} bit.".format("64" if is64bit else "32")
)
# 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(SYSTEM32, "oleaut32.dll"))
register64bitServer(os.path.join(SYSTEM32, "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
# There was no "Program Files (x86)" in Windows 7 32-bit, so we cover both cases
register32bitServer(os.path.join(
PROGRAM_FILES_X86 if is64bit else PROGRAM_FILES,
"Internet Explorer", "ieproxy.dll"
))
if is64bit:
registerServer(os.path.join(programFilesX86,'Internet Explorer','ieproxy.dll'),wow64=True)
register64bitServer(os.path.join(PROGRAM_FILES, "Internet Explorer", "ieproxy.dll"))
12 changes: 9 additions & 3 deletions source/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,17 @@ def onRunCOMRegistrationFixesCommand(self, evt):
log.error("Could not execute fixCOMRegistrations command",exc_info=True)
progressDialog.done()
del progressDialog
# Translators: The message displayed when the COM Registration Fixing tool completes.
gui.messageBox(_("COM Registration Fixing tool complete"),
# We get an F405 here because "gui may be undefined".
gui.messageBox( # noqa F405
_(
# Translators: The message displayed when the COM Registration Fixing tool completes.
"The COM Registration Fixing tool has finished. "
"It is highly recommended that you restart your computer now, to make sure the changes take full effect."
),
# Translators: The title of a dialog presented when the COM Registration Fixing tool is complete.
_("COM Registration Fixing Tool"),
wx.OK)
wx.OK
)

def onConfigProfilesCommand(self, evt):
if isInMessageBox:
Expand Down

0 comments on commit f156f38

Please sign in to comment.