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
@@ -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+
113174def 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
222320SLAVE_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+
240346def 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 (
0 commit comments