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.
1010For the latter two actions, one can perform actions prior to and/or after they take place.
1111"""
1212
13+ from enum import Enum
1314import globalVars
1415import winreg
1516import ctypes
3637from 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+
113173def 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
222332SLAVE_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+
240358def 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