Skip to content

Commit 63b06a1

Browse files
authored
Merge fa64276 into 2d352dd
2 parents 2d352dd + fa64276 commit 63b06a1

3 files changed

Lines changed: 184 additions & 52 deletions

File tree

source/config/__init__.py

Lines changed: 140 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# A part of NonVisual Desktop Access (NVDA)
2-
# Copyright (C) 2006-2021 NV Access Limited, Aleksey Sadovoy, Peter Vágner, Rui Batista, Zahari Yurukov,
2+
# Copyright (C) 2006-2022 NV Access Limited, Aleksey Sadovoy, Peter Vágner, Rui Batista, Zahari Yurukov,
33
# Joseph Lee, Babbage B.V., Łukasz Golonka, Julien Cochuyt
44
# This file is covered by the GNU General Public License.
55
# See the file COPYING for more details.
@@ -10,6 +10,7 @@
1010
For the latter two actions, one can perform actions prior to and/or after they take place.
1111
"""
1212

13+
from enum import Enum
1314
import globalVars
1415
import winreg
1516
import ctypes
@@ -73,43 +74,103 @@ def saveOnExit():
7374
except:
7475
pass
7576

76-
def isInstalledCopy():
77+
78+
class _RegKey(str, Enum):
79+
INSTALLED_COPY = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NVDA"
80+
RUN = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
81+
NVDA = r"SOFTWARE\NVDA"
82+
r"""
83+
The name of the registry key stored under HKEY_LOCAL_MACHINE where system wide NVDA settings are stored.
84+
Note that NVDA is a 32-bit application, so on X64 systems,
85+
this will evaluate to `r"SOFTWARE\WOW6432Node\nvda"`
86+
"""
87+
88+
89+
def isInstalledCopy() -> bool:
7790
"""Checks to see if this running copy of NVDA is installed on the system"""
7891
try:
79-
k=winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NVDA")
80-
instDir=winreg.QueryValueEx(k,"UninstallDirectory")[0]
92+
k = winreg.OpenKey(
93+
winreg.HKEY_LOCAL_MACHINE,
94+
_RegKey.INSTALLED_COPY
95+
)
96+
except FileNotFoundError:
97+
log.debug(
98+
f"Unable to find isInstalledCopy registry key {_RegKey.INSTALLED_COPY}"
99+
"- this is not an installed copy."
100+
)
101+
return False
102+
except WindowsError:
103+
log.error(
104+
f"Unable to open isInstalledCopy registry key {_RegKey.INSTALLED_COPY}",
105+
exc_info=True
106+
)
107+
return False
108+
109+
try:
110+
instDir = winreg.QueryValueEx(k, "UninstallDirectory")[0]
111+
except FileNotFoundError:
112+
log.debug(
113+
f"Unable to find UninstallDirectory value for {_RegKey.INSTALLED_COPY}"
114+
"- this may not be an installed copy."
115+
)
116+
return False
81117
except WindowsError:
118+
log.error("Unable to query isInstalledCopy registry key", exc_info=True)
82119
return False
120+
83121
winreg.CloseKey(k)
84122
try:
85123
return os.stat(instDir) == os.stat(globalVars.appDir)
86124
except WindowsError:
125+
log.error(
126+
"Failed to access the installed NVDA directory,"
127+
"or, a portable copy failed to access the current NVDA app directory",
128+
exc_info=True
129+
)
87130
return False
88131

89132

90-
#: #6864: The name of the subkey stored under NVDA_REGKEY where the value is stored
91-
#: which will make an installed NVDA load the user configuration either from the local or from the roaming application data profile.
92-
#: The registry value is unset by default.
93-
#: When setting it manually, a DWORD value is prefered.
94-
#: A value of 0 will evaluate to loading the configuration from the roaming application data (default).
95-
#: A value of 1 means loading the configuration from the local application data folder.
96-
#: @type: str
97-
CONFIG_IN_LOCAL_APPDATA_SUBKEY=u"configInLocalAppData"
133+
CONFIG_IN_LOCAL_APPDATA_SUBKEY = "configInLocalAppData"
134+
"""
135+
#6864: The name of the subkey stored under NVDA_REGKEY where the value is stored
136+
which will make an installed NVDA load the user configuration either from the local or from
137+
the roaming application data profile.
138+
The registry value is unset by default.
139+
When setting it manually, a DWORD value is preferred.
140+
A value of 0 will evaluate to loading the configuration from the roaming application data (default).
141+
A value of 1 means loading the configuration from the local application data folder.
142+
"""
143+
144+
145+
def getInstalledUserConfigPath() -> Optional[str]:
146+
try:
147+
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, _RegKey.NVDA)
148+
except WindowsError:
149+
log.error("Could not find nvda registry key", exc_info=True)
150+
configInLocalAppData = False
98151

99-
def getInstalledUserConfigPath():
100152
try:
101-
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, NVDA_REGKEY)
102153
configInLocalAppData = bool(winreg.QueryValueEx(k, CONFIG_IN_LOCAL_APPDATA_SUBKEY)[0])
154+
except FileNotFoundError:
155+
log.debug("Installed user config is not in local app data")
156+
configInLocalAppData = False
103157
except WindowsError:
104-
configInLocalAppData=False
158+
log.error(
159+
f"Could not query if user config in local app data {CONFIG_IN_LOCAL_APPDATA_SUBKEY}",
160+
exc_info=True
161+
)
162+
configInLocalAppData = False
105163
configParent = shlobj.SHGetKnownFolderPath(
106164
shlobj.FolderId.LOCAL_APP_DATA if configInLocalAppData else shlobj.FolderId.ROAMING_APP_DATA
107165
)
108166
try:
109167
return os.path.join(configParent, "nvda")
110168
except WindowsError:
169+
# (#PRnum) There is some uncertainty as to how this could be caused
170+
log.debugWarning("Installed user config is not in local app data", exc_info=True)
111171
return None
112172

173+
113174
def getUserDefaultConfigPath(useInstalledPathIfExists=False):
114175
"""Get the default path for the user configuration directory.
115176
This is the default path and doesn't reflect overriding from the command line,
@@ -181,22 +242,49 @@ def initConfigPath(configPath=None):
181242
if not os.path.isdir(subdir):
182243
os.makedirs(subdir)
183244

184-
RUN_REGKEY = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
185245

186-
def getStartAfterLogon():
246+
def getStartAfterLogon() -> bool:
187247
if (
188248
easeOfAccess.canConfigTerminateOnDesktopSwitch
189249
and easeOfAccess.willAutoStart(winreg.HKEY_CURRENT_USER)
190250
):
191251
return True
192252
try:
193-
k = winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_REGKEY)
194-
val = winreg.QueryValueEx(k, u"nvda")[0]
253+
k = winreg.OpenKey(winreg.HKEY_CURRENT_USER, _RegKey.RUN)
254+
except FileNotFoundError:
255+
log.debugWarning(
256+
f"Unable to find run registry key {_RegKey.RUN}",
257+
exc_info=True
258+
)
259+
return False
260+
except WindowsError:
261+
log.error(
262+
f"Unable to open run registry key {_RegKey.RUN}",
263+
exc_info=True
264+
)
265+
return False
266+
267+
try:
268+
val = winreg.QueryValueEx(k, "nvda")[0]
269+
except FileNotFoundError:
270+
log.debug("NVDA is not set to start after logon")
271+
return False
272+
except WindowsError:
273+
log.error("Failed to query NVDA key to set start after logon", exc_info=True)
274+
return False
275+
276+
try:
195277
return os.stat(val) == os.stat(sys.argv[0])
196-
except (WindowsError, OSError):
278+
except WindowsError:
279+
log.error(
280+
"Failed to access the current running NVDA directory,"
281+
" or the start after logon app directory",
282+
exc_info=True
283+
)
197284
return False
198285

199-
def setStartAfterLogon(enable):
286+
287+
def setStartAfterLogon(enable: bool) -> None:
200288
if getStartAfterLogon() == enable:
201289
return
202290
if easeOfAccess.canConfigTerminateOnDesktopSwitch:
@@ -208,35 +296,53 @@ def setStartAfterLogon(enable):
208296
run = False
209297
else:
210298
run = enable
211-
k = winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_REGKEY, 0, winreg.KEY_WRITE)
299+
k = winreg.OpenKey(winreg.HKEY_CURRENT_USER, _RegKey.RUN, 0, winreg.KEY_WRITE)
212300
if run:
213-
winreg.SetValueEx(k, u"nvda", None, winreg.REG_SZ, sys.argv[0])
301+
winreg.SetValueEx(k, "nvda", None, winreg.REG_SZ, sys.argv[0])
214302
else:
215303
try:
216-
winreg.DeleteValue(k, u"nvda")
304+
winreg.QueryValue(k, "nvda")
305+
except FileNotFoundError:
306+
log.debug(
307+
"The run registry key is not set for setStartAfterLogon."
308+
"This is expected for Windows 8+ which uses ease of access"
309+
)
310+
return
311+
try:
312+
winreg.DeleteValue(k, "nvda")
217313
except WindowsError:
218-
pass
219-
314+
log.error(
315+
"Couldn't unset registry key for nvda to start after logon.",
316+
exc_info=True
317+
)
220318

221319

222320
SLAVE_FILENAME = os.path.join(globalVars.appDir, "nvda_slave.exe")
223321

224-
#: The name of the registry key stored under HKEY_LOCAL_MACHINE where system wide NVDA settings are stored.
225-
#: Note that NVDA is a 32-bit application, so on X64 systems, this will evaluate to "SOFTWARE\WOW6432Node\nvda"
226-
NVDA_REGKEY = r"SOFTWARE\NVDA"
227322

228-
def getStartOnLogonScreen():
323+
def getStartOnLogonScreen() -> bool:
229324
if easeOfAccess.willAutoStart(winreg.HKEY_LOCAL_MACHINE):
230325
return True
231326
try:
232-
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, NVDA_REGKEY)
233-
return bool(winreg.QueryValueEx(k, u"startOnLogonScreen")[0])
327+
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, _RegKey.NVDA)
328+
except FileNotFoundError:
329+
log.debugWarning(f"Could not find NVDA reg key {_RegKey.NVDA}", exc_info=True)
234330
except WindowsError:
331+
log.error(f"Failed to open NVDA reg key {_RegKey.NVDA}", exc_info=True)
332+
try:
333+
return bool(winreg.QueryValueEx(k, "startOnLogonScreen")[0])
334+
except FileNotFoundError:
335+
log.debug(f"Could not find startOnLogonScreen value for {_RegKey.NVDA} - likely unset.")
336+
return False
337+
except WindowsError:
338+
log.error(f"Failed to query startOnLogonScreen value for {_RegKey.NVDA}", exc_info=True)
235339
return False
236340

237-
def _setStartOnLogonScreen(enable):
341+
342+
def _setStartOnLogonScreen(enable: bool) -> None:
238343
easeOfAccess.setAutoStart(winreg.HKEY_LOCAL_MACHINE, enable)
239344

345+
240346
def setSystemConfigToCurrentConfig():
241347
fromPath = globalVars.appArgs.configPath
242348
if ctypes.windll.shell32.IsUserAnAdmin():
@@ -286,6 +392,7 @@ def setStartOnLogonScreen(enable):
286392
# Try setting it directly.
287393
_setStartOnLogonScreen(enable)
288394
except WindowsError:
395+
log.debugWarning("Failed to set start on logon screens config.")
289396
# We probably don't have admin privs, so we need to elevate to do this using the slave.
290397
import systemUtils
291398
if systemUtils.execElevated(

source/easeOfAccess.py

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# A part of NonVisual Desktop Access (NVDA)
2-
# Copyright (C) 2014-2021 NV Access Limited
2+
# Copyright (C) 2014-2022 NV Access Limited
33
# This file is covered by the GNU General Public License.
44
# See the file COPYING for more details.
55

66
"""Utilities for working with the Windows Ease of Access Center.
77
"""
88

9+
from typing import List
10+
from logHandler import log
911
import winreg
10-
import ctypes
1112
import winUser
1213
import winVersion
1314

@@ -18,13 +19,18 @@
1819
APP_KEY_NAME = "nvda_nvda_v1"
1920
APP_KEY_PATH = r"%s\ATs\%s" % (ROOT_KEY, APP_KEY_NAME)
2021

21-
def isRegistered():
22+
23+
def isRegistered() -> bool:
2224
try:
2325
winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, APP_KEY_PATH, 0,
2426
winreg.KEY_READ | winreg.KEY_WOW64_64KEY)
2527
return True
28+
except FileNotFoundError:
29+
log.debug("Unable to find AT registry key")
2630
except WindowsError:
27-
return False
31+
log.error("Unable to open AT registry key", exc_info=True)
32+
return False
33+
2834

2935
def notify(signal):
3036
if not isRegistered():
@@ -55,28 +61,41 @@ def notify(signal):
5561
inputs.append(input)
5662
winUser.SendInput(inputs)
5763

58-
def willAutoStart(hkey):
64+
65+
def willAutoStart(hkey: winreg._KeyType) -> bool:
66+
return (APP_KEY_NAME in _getAutoStartConfiguration(hkey))
67+
68+
69+
def _getAutoStartConfiguration(hkey: winreg._KeyType) -> List[str]:
5970
try:
6071
k = winreg.OpenKey(hkey, ROOT_KEY, 0,
6172
winreg.KEY_READ | winreg.KEY_WOW64_64KEY)
62-
return (APP_KEY_NAME in
63-
winreg.QueryValueEx(k, "Configuration")[0].split(","))
73+
except FileNotFoundError:
74+
log.debug("Unable to find existing auto start registry key")
75+
return []
6476
except WindowsError:
65-
return False
77+
log.error("Unable to open auto start registry key for reading", exc_info=True)
78+
return []
6679

67-
def setAutoStart(hkey, enable):
68-
k = winreg.OpenKey(hkey, ROOT_KEY, 0,
69-
winreg.KEY_READ | winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY)
7080
try:
71-
conf = winreg.QueryValueEx(k, "Configuration")[0].split(",")
81+
conf: List[str] = winreg.QueryValueEx(k, "Configuration")[0].split(",")
82+
except FileNotFoundError:
83+
log.debug("Unable to find auto start configuration")
7284
except WindowsError:
73-
conf = []
85+
log.error("Unable to query auto start configuration", exc_info=True)
7486
else:
7587
if not conf[0]:
7688
# "".split(",") returns [""], so remove the empty string.
7789
del conf[0]
78-
changed = False
79-
if enable and APP_KEY_NAME not in conf:
90+
return conf
91+
return []
92+
93+
94+
def setAutoStart(hkey: winreg._KeyType, enable: bool) -> None:
95+
"""Raises `Union[WindowsError, FileNotFoundError]`"""
96+
conf = _getAutoStartConfiguration(hkey)
97+
currentlyEnabled = APP_KEY_NAME in conf
98+
if enable and not currentlyEnabled:
8099
conf.append(APP_KEY_NAME)
81100
changed = True
82101
elif not enable:
@@ -86,5 +105,11 @@ def setAutoStart(hkey, enable):
86105
except ValueError:
87106
pass
88107
if changed:
108+
k = winreg.OpenKey(
109+
hkey,
110+
ROOT_KEY,
111+
0,
112+
winreg.KEY_READ | winreg.KEY_WRITE | winreg.KEY_WOW64_64KEY
113+
)
89114
winreg.SetValueEx(k, "Configuration", None, winreg.REG_SZ,
90115
",".join(conf))

0 commit comments

Comments
 (0)