Repository: nvaccess/nvda

Issue:
NVDA no longer recognises that the system has switched to a secure desktop when dismissing the lock screen
### Steps to reproduce:
1. Press Windows+l to lock the computer.
2. Press escape to dismiss the lock screen.

### Actual behavior:
NVDA reports the Windows sign-in screen.

### Expected behavior:
NVDA should first say "Secure Desktop" before reporting the sign-in screen.

### System configuration
#### NVDA installed/portable/running from source:
Installed.

#### NVDA version:
2022.2.2
alpha-26424,2392d13c (2022.4.0.26424)

#### Windows version:
Windows 11 Version 21H2 (OS Build 22000.795)
Windows 10 Version 21H2 (OS Build 19044.1889)

### Other Information
While this doesn't seem to matter on the surface, it causes a significant problem for NVDA Remote. Because of this, NVDA Remote [doesn't know that the system has switched to a secure desktop](https://github.com/nvdaremote/nvdaremote/blob/master/addon/globalPlugins/remoteClient/__init__.py#L460) when the lock screen is dismissed and so it doesn't react accordingly. That means that the user can't interact with the sign-in screen remotely, effectively locking them out of their system.

This issue was introduced in either 2022.2.1 or 2022.2.2. My guess is the former. I haven't dug into this much, but looking at the commit log, I'm guessing that IAccessibleHandler.SecureDesktopNVDAObject needs to be treated as secure or something like that.


Discussion:
Does the same in Windows 7, just tried it.
 Brian

-- 
***@***.***
Sent via blueyonder.(Virgin media)
Please address personal E-mail to:-
***@***.***, putting 'Brian Gaff'
in the display name field.
----- Original Message ----- 
From: "James Teh" ***@***.***>
To: "nvaccess/nvda" ***@***.***>
Cc: "Subscribed" ***@***.***>
Sent: Thursday, September 01, 2022 11:58 AM
Subject: [nvaccess/nvda] NVDA no longer recognises that the system has 
switched to a secure desktop when dismissing the lock screen (Issue #14094)


> ### Steps to reproduce:
> 1. Press Windows+l to lock the computer.
> 2. Press escape to dismiss the lock screen.
>
> ### Actual behavior:
> NVDA reports the Windows sign-in screen.
>
> ### Expected behavior:
> NVDA should first say "Secure Desktop" before reporting the sign-in 
> screen.
>
> ### System configuration
> #### NVDA installed/portable/running from source:
> Installed.
>
> #### NVDA version:
> 2022.2.2
> alpha-26424,2392d13c (2022.4.0.26424)
>
> #### Windows version:
> Windows 11 Version 21H2 (OS Build 22000.795)
> Windows 10 Version 21H2 (OS Build 19044.1889)
>
> ### Other Information
> While this doesn't seem to matter on the surface, it causes a significant 
> problem for NVDA Remote. Because of this, NVDA Remote [doesn't know that 
> the system has switched to a secure 
> desktop](https://github.com/nvdaremote/nvdaremote/blob/master/addon/globalPlugins/remoteClient/__init__.py#L460) 
> when the lock screen is dismissed and so it doesn't react accordingly. 
> That means that the user can't interact with the sign-in screen remotely, 
> effectively locking them out of their system.
>
> This issue was introduced in either 2022.2.1 or 2022.2.2. My guess is the 
> former. I haven't dug into this much, but looking at the commit log, I'm 
> guessing that IAccessibleHandler.SecureDesktopNVDAObject needs to be 
> treated as secure or something like that.
>
> -- 
> Reply to this email directly or view it on GitHub:
> https://github.com/nvaccess/nvda/issues/14094
> You are receiving this because you are subscribed to this thread.
>
> Message ID: ***@***.***> 



Resolving patch:
diff --git a/source/IAccessibleHandler/__init__.py b/source/IAccessibleHandler/__init__.py
--- a/source/IAccessibleHandler/__init__.py
+++ b/source/IAccessibleHandler/__init__.py
@@ -48,6 +48,7 @@
 import keyboardHandler
 from logHandler import log
 import mouseHandler
+from NVDAObjects import NVDAObject
 import NVDAObjects.IAccessible
 import NVDAObjects.window
 import winUser
@@ -773,7 +774,31 @@ def processFocusNVDAEvent(obj, force=False):
 	return True
 
 
-class SecureDesktopNVDAObject(NVDAObjects.window.Desktop):
+class SecureDesktopNVDAObject(NVDAObject):
+	"""
+	Used to indicate to the user and to API consumers (including NVDA remote),
+	that the user has switched to a secure desktop.
+	This is triggered when Windows notification EVENT_SYSTEM_DESKTOPSWITCH
+	notifies that the desktop has changed, and is handled via a gainFocus event.
+	The gainFocus event causes NVDA to enter sleep mode as the secure mode
+	NVDA instance starts on the secure screen.
+
+	This object is not backed by a valid MSAA object as the Secure Desktop
+	object should not be accessed via NVDA.
+	The minimal functionality has been added to support backwards compatibility.
+	"""
+
+	def __init__(self, windowHandle: int):
+		"""
+		@param windowHandle: to retain backwards compatibility,
+		unused as this object is not backed by a valid MSAA object.
+		This object just serves as a minimal API endpoint.
+		"""
+		self._windowHandle = windowHandle
+		super().__init__()
+
+	def _get_processID(self) -> int:
+		return 0
 
 	def findOverlayClasses(self, clsList):
 		clsList.append(SecureDesktopNVDAObject)
diff --git a/source/eventHandler.py b/source/eventHandler.py
--- a/source/eventHandler.py
+++ b/source/eventHandler.py
@@ -303,6 +303,8 @@ def executeEvent(
 
 
 def doPreGainFocus(obj: "NVDAObjects.NVDAObject", sleepMode: bool = False) -> bool:
+	from IAccessibleHandler import SecureDesktopNVDAObject
+
 	if objectBelowLockScreenAndWindowsIsLocked(
 		obj,
 		shouldLog=config.conf["debugLog"]["events"],
@@ -326,7 +328,16 @@ def doPreGainFocus(obj: "NVDAObjects.NVDAObject", sleepMode: bool = False) -> bo
 		# - api.getFocusAncestors() via api.setFocusObject() called in doPreGainFocus
 		speech._manager.removeCancelledSpeechCommands()
 
-	if api.getFocusDifferenceLevel() <= 1:
+	if (
+		api.getFocusDifferenceLevel() <= 1
+		# This object should not set off a foreground event.
+		# SecureDesktopNVDAObject uses a gainFocus event to trigger NVDA
+		# to sleep as the secure instance of NVDA starts for the
+		# secure desktop.
+		# The newForeground object fetches from the User Desktop,
+		# not the secure desktop.
+		and not isinstance(obj, SecureDesktopNVDAObject)
+	):
 		newForeground=api.getDesktopObject().objectInForeground()
 		if not newForeground:
 			log.debugWarning("Can not get real foreground, resorting to focus ancestors")
diff --git a/source/utils/security.py b/source/utils/security.py
--- a/source/utils/security.py
+++ b/source/utils/security.py
@@ -119,6 +119,7 @@ def isObjectAboveLockScreen(obj: "NVDAObjects.NVDAObject") -> bool:
 	as it may contain sensitive information.
 	"""
 	import appModuleHandler
+	from IAccessibleHandler import SecureDesktopNVDAObject
 	from NVDAObjects.IAccessible import TaskListIcon
 
 	foregroundWindow = winUser.getForegroundWindow()
@@ -132,6 +133,10 @@ def isObjectAboveLockScreen(obj: "NVDAObjects.NVDAObject") -> bool:
 		# The task switcher window does not become the foreground process on the lock screen,
 		# so we must whitelist it explicitly.
 		isinstance(obj, TaskListIcon)
+		# Secure Desktop Object.
+		# Used to indicate to the user and to API consumers (including NVDA remote) via gainFocus,
+		# that the user has switched to a secure desktop.
+		or isinstance(obj, SecureDesktopNVDAObject)
 	):
 		return True
 


Test patch: