Skip to content

Commit 28d8aa5

Browse files
Support browse mode and other in-process scenarios for AMD64 apps when running on ARM64 Windows (#14449)
Although NVDA fully functions on Windows 10 ARM64, including interacting with both ARM64 and x86-emulated apps, on Windows 11 particular in-process NVDA features such as browse mode is not available when interacting with AMD64-emulated apps. For instance, Running AMD64 builds of Firefox, or 1Password, or Chrome on Windows 11 ARM64, browse mode is not available. The reason for this is that on ARM64 Windows, NVDA only launches an ARM64 nvdaHelperRemoteLoader process for injecting NVDAHelper, not an AMD64 one. This is because in Windows 10 running AMD64 apps was not supported by the OS. NVDA should fully support AMD64-emulated apps on windows 11. Description of user facing changes On Windows 11 ARM64, NVDA's browse mode is now supported in AMD64 apps such as Firefox, Google Chrome, and 1Password. Description of development approach • NVDAHelper.py's versionedLib64Path variable has been replaced by versionLibAMD64Path and versionedLibARM64Path variables. • NVDAHelper.py's RemoteLoader64 class has been renamed to RemoteLoader, and it now takes a loader directory path on construction, so that it is possible to launch NVDAHelperRemoteLoader.exe in either lib64 (AMD64) or libARM64. • When initializing NVDAHelper, on ARM64, both ARM64 and AMD64 versions of NVDAhelperRemoteLoader are now started, and both are cleaned up on termination.
1 parent 304ea7a commit 28d8aa5

3 files changed

Lines changed: 39 additions & 16 deletions

File tree

‎source/NVDAHelper.py‎

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
# This file is covered by the GNU General Public License.
55
# See the file COPYING for more details.
66

7+
from typing import Optional
78
import os
89
import winreg
910
import msvcrt
11+
import winVersion
1012
import versionInfo
1113
import winKernel
1214
import config
@@ -31,19 +33,20 @@
3133
from utils.security import _isLockScreenModeActive
3234

3335
versionedLibPath = os.path.join(globalVars.appDir, 'lib')
34-
if os.environ.get('PROCESSOR_ARCHITEW6432') == 'ARM64':
35-
versionedLib64Path = os.path.join(globalVars.appDir, 'libArm64')
36-
else:
37-
versionedLib64Path = os.path.join(globalVars.appDir, 'lib64')
36+
versionedLibARM64Path = os.path.join(globalVars.appDir, 'libArm64')
37+
versionedLibAMD64Path = os.path.join(globalVars.appDir, 'lib64')
3838

3939

4040
if not NVDAState.isRunningAsSource():
4141
# When running as a py2exe build, libraries are in a version-specific directory
4242
versionedLibPath=os.path.join(versionedLibPath,versionInfo.version)
43-
versionedLib64Path=os.path.join(versionedLib64Path,versionInfo.version)
43+
versionedLibAMD64Path = os.path.join(versionedLibAMD64Path, versionInfo.version)
44+
versionedLibARM64Path = os.path.join(versionedLibARM64Path, versionInfo.version)
45+
4446

4547
_remoteLib=None
46-
_remoteLoader64=None
48+
_remoteLoaderAMD64: "Optional[_RemoteLoader]" = None
49+
_remoteLoaderARM64: "Optional[_RemoteLoader]" = None
4750
localLib=None
4851
generateBeep=None
4952
VBuf_getTextInRange=None
@@ -480,9 +483,9 @@ def nvdaControllerInternal_openConfigDirectory():
480483
return 0
481484

482485

483-
class RemoteLoader64(object):
486+
class _RemoteLoader:
484487

485-
def __init__(self):
488+
def __init__(self, loaderDir: str):
486489
# Create a pipe so we can write to stdin of the loader process.
487490
pipeReadOrig, self._pipeWrite = winKernel.CreatePipe(None, 0)
488491
# Make the read end of the pipe inheritable.
@@ -500,7 +503,9 @@ def __init__(self):
500503
# Therefore, explicitly specify our own process token, which causes them to be inherited.
501504
token = winKernel.OpenProcessToken(winKernel.GetCurrentProcess(), winKernel.MAXIMUM_ALLOWED)
502505
try:
503-
winKernel.CreateProcessAsUser(token, None, os.path.join(versionedLib64Path,u"nvdaHelperRemoteLoader.exe"), None, None, True, None, None, None, si, pi)
506+
loaderPath = os.path.join(loaderDir, "nvdaHelperRemoteLoader.exe")
507+
log.debug(f"Starting {loaderPath}")
508+
winKernel.CreateProcessAsUser(token, None, loaderPath, None, None, True, None, None, None, si, pi)
504509
# We don't need the thread handle.
505510
winKernel.closeHandle(pi.hThread)
506511
self._process = pi.hProcess
@@ -522,8 +527,10 @@ def terminate(self):
522527
winKernel.waitForSingleObject(self._process, winKernel.INFINITE)
523528
winKernel.closeHandle(self._process)
524529

530+
525531
def initialize():
526-
global _remoteLib, _remoteLoader64, localLib, generateBeep, VBuf_getTextInRange, lastLanguageID, lastLayoutString
532+
global _remoteLib, _remoteLoaderAMD64, _remoteLoaderARM64
533+
global localLib, generateBeep, VBuf_getTextInRange, lastLanguageID, lastLayoutString
527534
hkl=c_ulong(windll.User32.GetKeyboardLayout(0)).value
528535
lastLanguageID=winUser.LOWORD(hkl)
529536
KL_NAMELENGTH=9
@@ -587,20 +594,33 @@ def initialize():
587594
log.error("Error installing IA2 support")
588595
#Manually start the in-process manager thread for this NVDA main thread now, as a slow system can cause this action to confuse WX
589596
_remoteLib.initInprocManagerThreadIfNeeded()
590-
if os.environ.get('PROCESSOR_ARCHITEW6432') in ('AMD64', 'ARM64'):
591-
_remoteLoader64=RemoteLoader64()
597+
versionedLibARM64Path
598+
arch = os.environ.get('PROCESSOR_ARCHITEW6432')
599+
if arch == 'AMD64':
600+
_remoteLoaderAMD64 = _RemoteLoader(versionedLibAMD64Path)
601+
elif arch == 'ARM64':
602+
_remoteLoaderARM64 = _RemoteLoader(versionedLibARM64Path)
603+
# Windows on ARM from Windows 11 supports running AMD64 apps.
604+
# Thus we also need to be able to inject into these.
605+
if winVersion.getWinVer() >= winVersion.WIN11:
606+
_remoteLoaderAMD64 = _RemoteLoader(versionedLibAMD64Path)
607+
592608

593609
def terminate():
594-
global _remoteLib, _remoteLoader64, localLib, generateBeep, VBuf_getTextInRange
610+
global _remoteLib, _remoteLoaderAMD64, _remoteLoaderARM64
611+
global localLib, generateBeep, VBuf_getTextInRange
595612
if not config.isAppX:
596613
if not _remoteLib.uninstallIA2Support():
597614
log.debugWarning("Error uninstalling IA2 support")
598615
if _remoteLib.injection_terminate() == 0:
599616
raise RuntimeError("Error terminating NVDAHelperRemote")
600617
_remoteLib=None
601-
if _remoteLoader64:
602-
_remoteLoader64.terminate()
603-
_remoteLoader64=None
618+
if _remoteLoaderAMD64:
619+
_remoteLoaderAMD64.terminate()
620+
_remoteLoaderAMD64 = None
621+
if _remoteLoaderARM64:
622+
_remoteLoaderARM64.terminate()
623+
_remoteLoaderARM64 = None
604624
generateBeep=None
605625
VBuf_getTextInRange=None
606626
localLib.nvdaHelperLocal_terminate()

‎user_docs/en/changes.t2t‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ What's New in NVDA
1414
- Added an experimental option to leverage the UIA notification support in Windows Terminal to report new or changed text in the terminal, resulting in improved stability and responsivity. (#13781)
1515
- Consult the user guide for limitations of this experimental option.
1616
-
17+
- On Windows 11 ARM64, browse mode is now available in AMD64 apps such as Firefox, Google Chrome and 1Password. (#14397)
1718
-
1819

1920

@@ -86,6 +87,7 @@ Please open a GitHub issue if your Add-on has an issue with updating to the new
8687
- ``autoTether`` has been removed; ``tetherTo`` can now take the value "auto" instead.
8788
-
8889
-
90+
- The ``NVDAHelper.RemoteLoader64`` class has been removed with no replacement. (#14449)
8991
-
9092

9193

‎user_docs/en/userGuide.t2t‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Major highlights include:
4040
- Operating Systems: all 32-bit and 64-bit editions of Windows 7, Windows 8, Windows 8.1, Windows 10, Windows 11, and all Server Operating Systems starting from Windows Server 2008 R2.
4141
- For Windows 7, NVDA requires Service Pack 1 or higher.
4242
- For Windows Server 2008 R2, NVDA requires Service Pack 1 or higher.
43+
- both AMD64 and ARM64 variants of Windows are supported.
4344
-
4445
- at least 150 MB of storage space.
4546
-

0 commit comments

Comments
 (0)