Skip to content

Commit c80b4ce

Browse files
authored
Merge 879bca1 into 9092227
2 parents 9092227 + 879bca1 commit c80b4ce

3 files changed

Lines changed: 206 additions & 61 deletions

File tree

source/config/__init__.py

Lines changed: 156 additions & 37 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
@@ -36,11 +37,10 @@
3637
from typing import Any, Dict, List, Optional, Set
3738

3839
#: True if NVDA is running as a Windows Store Desktop Bridge application
39-
isAppX=False
40+
isAppX: bool = False
4041

4142
#: The active configuration, C{None} if it has not yet been loaded.
42-
#: @type: ConfigManager
43-
conf = None
43+
conf: Optional["ConfigManager"] = None
4444

4545
#: Notifies when the configuration profile is switched.
4646
#: This allows components and add-ons to apply changes required by the new configuration.
@@ -73,43 +73,103 @@ def saveOnExit():
7373
except:
7474
pass
7575

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

89131

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

99-
def getInstalledUserConfigPath():
100151
try:
101-
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, NVDA_REGKEY)
102152
configInLocalAppData = bool(winreg.QueryValueEx(k, CONFIG_IN_LOCAL_APPDATA_SUBKEY)[0])
153+
except FileNotFoundError:
154+
log.debug("Installed user config is not in local app data")
155+
configInLocalAppData = False
103156
except WindowsError:
104-
configInLocalAppData=False
157+
log.error(
158+
f"Could not query if user config in local app data {CONFIG_IN_LOCAL_APPDATA_SUBKEY}",
159+
exc_info=True
160+
)
161+
configInLocalAppData = False
105162
configParent = shlobj.SHGetKnownFolderPath(
106163
shlobj.FolderId.LOCAL_APP_DATA if configInLocalAppData else shlobj.FolderId.ROAMING_APP_DATA
107164
)
108165
try:
109166
return os.path.join(configParent, "nvda")
110167
except WindowsError:
168+
# (#PRnum) There is some uncertainty as to how this could be caused
169+
log.debugWarning("Installed user config is not in local app data", exc_info=True)
111170
return None
112171

172+
113173
def getUserDefaultConfigPath(useInstalledPathIfExists=False):
114174
"""Get the default path for the user configuration directory.
115175
This is the default path and doesn't reflect overriding from the command line,
@@ -181,22 +241,62 @@ def initConfigPath(configPath=None):
181241
if not os.path.isdir(subdir):
182242
os.makedirs(subdir)
183243

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

186-
def getStartAfterLogon():
245+
def getStartAfterLogon() -> bool:
187246
if (
188247
easeOfAccess.canConfigTerminateOnDesktopSwitch
189248
and easeOfAccess.willAutoStart(winreg.HKEY_CURRENT_USER)
190249
):
191250
return True
192251
try:
193-
k = winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_REGKEY)
194-
val = winreg.QueryValueEx(k, u"nvda")[0]
195-
return os.stat(val) == os.stat(sys.argv[0])
196-
except (WindowsError, OSError):
252+
k = winreg.OpenKey(winreg.HKEY_CURRENT_USER, _RegKey.RUN)
253+
except FileNotFoundError:
254+
log.debugWarning(
255+
f"Unable to find run registry key {_RegKey.RUN}",
256+
exc_info=True
257+
)
258+
return False
259+
except WindowsError:
260+
log.error(
261+
f"Unable to open run registry key {_RegKey.RUN}",
262+
exc_info=True
263+
)
264+
return False
265+
266+
try:
267+
val = winreg.QueryValueEx(k, "nvda")[0]
268+
except FileNotFoundError:
269+
log.debug("NVDA is not set to start after logon")
270+
return False
271+
except WindowsError:
272+
log.error("Failed to query NVDA key to set start after logon", exc_info=True)
273+
return False
274+
275+
try:
276+
startAfterLogonPath = os.stat(val)
277+
except WindowsError:
278+
log.error(
279+
"Failed to access the start after logon directory.",
280+
exc_info=True
281+
)
282+
return False
283+
284+
try:
285+
currentSourcePath = os.stat(sys.argv[0])
286+
except FileNotFoundError:
287+
log.debug("Failed to access the current running NVDA directory.")
288+
return False
289+
except WindowsError:
290+
log.error(
291+
"Failed to access the current running NVDA directory.",
292+
exc_info=True
293+
)
197294
return False
198295

199-
def setStartAfterLogon(enable):
296+
return currentSourcePath == startAfterLogonPath
297+
298+
299+
def setStartAfterLogon(enable: bool) -> None:
200300
if getStartAfterLogon() == enable:
201301
return
202302
if easeOfAccess.canConfigTerminateOnDesktopSwitch:
@@ -208,35 +308,53 @@ def setStartAfterLogon(enable):
208308
run = False
209309
else:
210310
run = enable
211-
k = winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_REGKEY, 0, winreg.KEY_WRITE)
311+
k = winreg.OpenKey(winreg.HKEY_CURRENT_USER, _RegKey.RUN, 0, winreg.KEY_WRITE)
212312
if run:
213-
winreg.SetValueEx(k, u"nvda", None, winreg.REG_SZ, sys.argv[0])
313+
winreg.SetValueEx(k, "nvda", None, winreg.REG_SZ, sys.argv[0])
214314
else:
215315
try:
216-
winreg.DeleteValue(k, u"nvda")
316+
winreg.QueryValue(k, "nvda")
317+
except FileNotFoundError:
318+
log.debug(
319+
"The run registry key is not set for setStartAfterLogon."
320+
"This is expected for Windows 8+ which uses ease of access"
321+
)
322+
return
323+
try:
324+
winreg.DeleteValue(k, "nvda")
217325
except WindowsError:
218-
pass
219-
326+
log.error(
327+
"Couldn't unset registry key for nvda to start after logon.",
328+
exc_info=True
329+
)
220330

221331

222332
SLAVE_FILENAME = os.path.join(globalVars.appDir, "nvda_slave.exe")
223333

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"
227334

228-
def getStartOnLogonScreen():
335+
def getStartOnLogonScreen() -> bool:
229336
if easeOfAccess.willAutoStart(winreg.HKEY_LOCAL_MACHINE):
230337
return True
231338
try:
232-
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, NVDA_REGKEY)
233-
return bool(winreg.QueryValueEx(k, u"startOnLogonScreen")[0])
339+
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, _RegKey.NVDA)
340+
except FileNotFoundError:
341+
log.debugWarning(f"Could not find NVDA reg key {_RegKey.NVDA}", exc_info=True)
234342
except WindowsError:
343+
log.error(f"Failed to open NVDA reg key {_RegKey.NVDA}", exc_info=True)
344+
try:
345+
return bool(winreg.QueryValueEx(k, "startOnLogonScreen")[0])
346+
except FileNotFoundError:
347+
log.debug(f"Could not find startOnLogonScreen value for {_RegKey.NVDA} - likely unset.")
348+
return False
349+
except WindowsError:
350+
log.error(f"Failed to query startOnLogonScreen value for {_RegKey.NVDA}", exc_info=True)
235351
return False
236352

237-
def _setStartOnLogonScreen(enable):
353+
354+
def _setStartOnLogonScreen(enable: bool) -> None:
238355
easeOfAccess.setAutoStart(winreg.HKEY_LOCAL_MACHINE, enable)
239356

357+
240358
def setSystemConfigToCurrentConfig():
241359
fromPath = globalVars.appArgs.configPath
242360
if ctypes.windll.shell32.IsUserAnAdmin():
@@ -286,6 +404,7 @@ def setStartOnLogonScreen(enable):
286404
# Try setting it directly.
287405
_setStartOnLogonScreen(enable)
288406
except WindowsError:
407+
log.debugWarning("Failed to set start on logon screens config.")
289408
# We probably don't have admin privs, so we need to elevate to do this using the slave.
290409
import systemUtils
291410
if systemUtils.execElevated(

0 commit comments

Comments
 (0)