SEBWIN-714, #606: Implemented basic cursor functionality. Minor refactoring of registry and file system dialog classes.
This commit is contained in:
		
							parent
							
								
									fa16710bdb
								
							
						
					
					
						commit
						722d84978c
					
				
					 22 changed files with 392 additions and 202 deletions
				
			
		| 
						 | 
				
			
			@ -335,7 +335,29 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
			var timer = new System.Timers.Timer();
 | 
			
		||||
 | 
			
		||||
			timer.AutoReset = false;
 | 
			
		||||
			timer.Elapsed += (o, args) =>
 | 
			
		||||
			timer.Elapsed += (o, args) => VerifyApplicationIntegrity();
 | 
			
		||||
			timer.Interval = TEN_MINUTES + (new Random().NextDouble() * FIVE_MINUTES);
 | 
			
		||||
			timer.Start();
 | 
			
		||||
 | 
			
		||||
			if (registry.TryGetNames(RegistryValue.UserHive.Cursors_Key, out var names))
 | 
			
		||||
			{
 | 
			
		||||
				foreach (var name in names)
 | 
			
		||||
				{
 | 
			
		||||
					registry.StartMonitoring(RegistryValue.UserHive.Cursors_Key, name);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				logger.Warn("Failed to start monitoring cursor registry values!");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (Settings.Service.IgnoreService)
 | 
			
		||||
			{
 | 
			
		||||
				registry.StartMonitoring(RegistryValue.MachineHive.EaseOfAccess_Key, RegistryValue.MachineHive.EaseOfAccess_Name);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void VerifyApplicationIntegrity()
 | 
			
		||||
		{
 | 
			
		||||
			logger.Info($"Attempting to verify application integrity...");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -355,14 +377,6 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
			{
 | 
			
		||||
				logger.Warn("Failed to verify application integrity!");
 | 
			
		||||
			}
 | 
			
		||||
			};
 | 
			
		||||
			timer.Interval = TEN_MINUTES + (new Random().NextDouble() * FIVE_MINUTES);
 | 
			
		||||
			timer.Start();
 | 
			
		||||
 | 
			
		||||
			if (Settings.Service.IgnoreService)
 | 
			
		||||
			{
 | 
			
		||||
				registry.StartMonitoring(RegistryValue.MachineHive.EaseOfAccess_Key, RegistryValue.MachineHive.EaseOfAccess_Name);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void VerifySessionIntegrity()
 | 
			
		||||
| 
						 | 
				
			
			@ -701,9 +715,55 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
			splashScreen.UpdateStatus(status, true);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void Registry_ValueChanged(object oldValue, object newValue)
 | 
			
		||||
		private void Registry_ValueChanged(string key, string name, object oldValue, object newValue)
 | 
			
		||||
		{
 | 
			
		||||
			logger.Warn($"The ease of access registry value has changed from '{oldValue}' to '{newValue}'! Attempting to show lock screen...");
 | 
			
		||||
			if (key == RegistryValue.UserHive.Cursors_Key)
 | 
			
		||||
			{
 | 
			
		||||
				HandleCursorRegistryChange(key, name, oldValue, newValue);
 | 
			
		||||
			}
 | 
			
		||||
			else if (key == RegistryValue.MachineHive.EaseOfAccess_Key)
 | 
			
		||||
			{
 | 
			
		||||
				HandleEaseOfAccessRegistryChange(key, name, oldValue, newValue);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void HandleCursorRegistryChange(string key, string name, object oldValue, object newValue)
 | 
			
		||||
		{
 | 
			
		||||
			logger.Warn($@"The cursor registry value '{key}\{name}' has changed from '{oldValue}' to '{newValue}'! Attempting to show lock screen...");
 | 
			
		||||
 | 
			
		||||
			if (!sessionLocked)
 | 
			
		||||
			{
 | 
			
		||||
				var message = text.Get(TextKey.LockScreen_CursorMessage);
 | 
			
		||||
				var title = text.Get(TextKey.LockScreen_Title);
 | 
			
		||||
				var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_CursorContinueOption) };
 | 
			
		||||
				var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_CursorTerminateOption) };
 | 
			
		||||
 | 
			
		||||
				sessionLocked = true;
 | 
			
		||||
				registry.StopMonitoring(key, name);
 | 
			
		||||
 | 
			
		||||
				var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
 | 
			
		||||
 | 
			
		||||
				if (result.OptionId == continueOption.Id)
 | 
			
		||||
				{
 | 
			
		||||
					logger.Info("The session will be allowed to resume as requested by the user...");
 | 
			
		||||
				}
 | 
			
		||||
				else if (result.OptionId == terminateOption.Id)
 | 
			
		||||
				{
 | 
			
		||||
					logger.Info("Attempting to shutdown as requested by the user...");
 | 
			
		||||
					TryRequestShutdown();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				sessionLocked = false;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				logger.Info("Lock screen is already active.");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void HandleEaseOfAccessRegistryChange(string key, string name, object oldValue, object newValue)
 | 
			
		||||
		{
 | 
			
		||||
			logger.Warn($@"The ease of access registry value '{key}\{name}' has changed from '{oldValue}' to '{newValue}'! Attempting to show lock screen...");
 | 
			
		||||
 | 
			
		||||
			if (!sessionLocked)
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -713,7 +773,7 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
				var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_EaseOfAccessTerminateOption) };
 | 
			
		||||
 | 
			
		||||
				sessionLocked = true;
 | 
			
		||||
				registry.StopMonitoring();
 | 
			
		||||
				registry.StopMonitoring(key, name);
 | 
			
		||||
 | 
			
		||||
				var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,8 +94,9 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
			InitializeLogging();
 | 
			
		||||
			InitializeText();
 | 
			
		||||
 | 
			
		||||
			uiFactory = BuildUserInterfaceFactory();
 | 
			
		||||
			var registry = new Registry(ModuleLogger(nameof(Registry)));
 | 
			
		||||
 | 
			
		||||
			uiFactory = BuildUserInterfaceFactory();
 | 
			
		||||
			actionCenter = uiFactory.CreateActionCenter();
 | 
			
		||||
			context = new ClientContext();
 | 
			
		||||
			messageBox = BuildMessageBox();
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +104,7 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
			networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods);
 | 
			
		||||
			powerSupply = new PowerSupply(ModuleLogger(nameof(PowerSupply)));
 | 
			
		||||
			runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client);
 | 
			
		||||
			systemInfo = new SystemInfo();
 | 
			
		||||
			systemInfo = new SystemInfo(registry);
 | 
			
		||||
			taskbar = uiFactory.CreateTaskbar(ModuleLogger("Taskbar"));
 | 
			
		||||
			taskview = uiFactory.CreateTaskview();
 | 
			
		||||
			userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +117,6 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
			var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
 | 
			
		||||
			var fileSystemDialog = BuildFileSystemDialog();
 | 
			
		||||
			var hashAlgorithm = new HashAlgorithm();
 | 
			
		||||
			var registry = new Registry(ModuleLogger(nameof(Registry)));
 | 
			
		||||
			var splashScreen = uiFactory.CreateSplashScreen();
 | 
			
		||||
			var systemMonitor = new SystemMonitor(ModuleLogger(nameof(SystemMonitor)));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -335,9 +335,9 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
			switch (uiMode)
 | 
			
		||||
			{
 | 
			
		||||
				case UserInterfaceMode.Mobile:
 | 
			
		||||
					return new Mobile.FileSystemDialogFactory(text);
 | 
			
		||||
					return new Mobile.FileSystemDialogFactory(systemInfo, text);
 | 
			
		||||
				default:
 | 
			
		||||
					return new Desktop.FileSystemDialogFactory(text);
 | 
			
		||||
					return new Desktop.FileSystemDialogFactory(systemInfo, text);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,6 +68,9 @@ namespace SafeExamBrowser.I18n.Contracts
 | 
			
		|||
		LockScreen_ApplicationsAllowOption,
 | 
			
		||||
		LockScreen_ApplicationsMessage,
 | 
			
		||||
		LockScreen_ApplicationsTerminateOption,
 | 
			
		||||
		LockScreen_CursorContinueOption,
 | 
			
		||||
		LockScreen_CursorMessage,
 | 
			
		||||
		LockScreen_CursorTerminateOption,
 | 
			
		||||
		LockScreen_DisplayConfigurationContinueOption,
 | 
			
		||||
		LockScreen_DisplayConfigurationMessage,
 | 
			
		||||
		LockScreen_DisplayConfigurationTerminateOption,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,6 +162,15 @@
 | 
			
		|||
    <Entry key="LockScreen_ApplicationsTerminateOption">
 | 
			
		||||
        Safe Exam Browser beenden. WARNUNG: Sie werden keine Möglichkeit haben, Daten zu speichern oder weitere Aktionen auszuführen, SEB wird sofort beendet!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorContinueOption">
 | 
			
		||||
        Die Cursor-Konfiguration vorübergehend zulassen. Dies gilt nur für die momentan laufende Sitzung!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorMessage">
 | 
			
		||||
        Eine verbotene Cursor-Konfiguration wurde detektiert. Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren.
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorTerminateOption">
 | 
			
		||||
        Safe Exam Browser beenden. WARNUNG: Sie werden keine Möglichkeit haben, Daten zu speichern oder weitere Aktionen auszuführen, SEB wird sofort beendet!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_DisplayConfigurationContinueOption">
 | 
			
		||||
        Bildschirm-Konfiguration temporär erlauben. Dies gilt nur für die momentan laufende Sitzung!
 | 
			
		||||
    </Entry>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,6 +162,15 @@
 | 
			
		|||
    <Entry key="LockScreen_ApplicationsTerminateOption">
 | 
			
		||||
        Terminate Safe Exam Browser. WARNING: There will be no possibility to save data or perform any further actions, the shutdown will be initiated immediately!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorContinueOption">
 | 
			
		||||
        Temporarily allow the cursor configuration. This applies only to the currently running session!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorMessage">
 | 
			
		||||
        A prohibited cursor configuration has been detected. In order to unlock SEB, please select one of the available options and enter the correct unlock password.
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorTerminateOption">
 | 
			
		||||
        Terminate Safe Exam Browser. WARNING: There will be no possibility to save data or perform any further actions, the shutdown will be initiated immediately!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_DisplayConfigurationContinueOption">
 | 
			
		||||
        Temporarily allow the display configuration. This applies only to the currently running session!
 | 
			
		||||
    </Entry>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,6 +162,15 @@
 | 
			
		|||
    <Entry key="LockScreen_ApplicationsTerminateOption">
 | 
			
		||||
        Finalizar Safe Exam Browser. ADVERTENCIA: No habrá posibilidad de guardar datos o realizar ninguna otra acción, ¡el cierre se iniciará inmediatamente!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorContinueOption">
 | 
			
		||||
        Permitir temporalmente la configuración del cursor. Esto sólo se aplica a la sesión que se está ejecutando en ese momento.
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorMessage">
 | 
			
		||||
        Se ha detectado una configuración de cursor prohibida. Para desbloquear SEB, seleccione una de las opciones disponibles e introduzca la contraseña de desbloqueo correcta.
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorTerminateOption">
 | 
			
		||||
        Finalizar Safe Exam Browser. ADVERTENCIA: No habrá posibilidad de guardar datos o realizar ninguna otra acción, ¡el cierre se iniciará inmediatamente!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_DisplayConfigurationContinueOption">
 | 
			
		||||
        Permitir temporalmente la configuración de la pantalla. Esto sólo se aplica a la sesión que se está ejecutando en ese momento.
 | 
			
		||||
    </Entry>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,6 +162,15 @@
 | 
			
		|||
    <Entry key="LockScreen_ApplicationsTerminateOption">
 | 
			
		||||
        Terminer Safe Exam Browser. AVERTISSEMENT: Il n'y aura aucune possibilité de sauvegarder les données ou d'effectuer d'autres actions, la fermeture sera initiée immédiatement!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorContinueOption">
 | 
			
		||||
        Autoriser temporairement la configuration du curseur. Ceci s'applique uniquement à la session en cours!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorMessage">
 | 
			
		||||
        Une configuration interdite du curseur a été détectée. Pour déverrouiller SEB, veuillez sélectionner l'une des options disponibles et saisir le mot de passe de déverrouillage correct.
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorTerminateOption">
 | 
			
		||||
        Terminer Safe Exam Browser. AVERTISSEMENT: Il n'y aura aucune possibilité de sauvegarder les données ou d'effectuer d'autres actions, la fermeture sera initiée immédiatement!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_DisplayConfigurationContinueOption">
 | 
			
		||||
        Autoriser temporairement la configuration de l'affichage. Ceci s'applique uniquement à la session en cours!
 | 
			
		||||
    </Entry>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,6 +162,15 @@
 | 
			
		|||
    <Entry key="LockScreen_ApplicationsTerminateOption">
 | 
			
		||||
        Terminare Safe Exam Browser. ATTENZIONE: Non sarà possibile salvare i dati o eseguire ulteriori azioni, la chiusura verrà avviata immediatamente!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorContinueOption">
 | 
			
		||||
        Consente temporaneamente la configurazione del cursore. Questo vale solo per la sessione in corso!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorMessage">
 | 
			
		||||
        È stata rilevata una configurazione del cursore non consentita. Per sbloccare SEB, seleziona una delle opzioni disponibili e inserisci la password di sblocco corretta.
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorTerminateOption">
 | 
			
		||||
        Terminare Safe Exam Browser. ATTENZIONE: Non sarà possibile salvare i dati o eseguire ulteriori azioni, la chiusura verrà avviata immediatamente!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_DisplayConfigurationContinueOption">
 | 
			
		||||
        Consenti temporaneamente la configurazione del display. Questo vale solo per la sessione attualmente in esecuzione!
 | 
			
		||||
    </Entry>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -147,6 +147,15 @@
 | 
			
		|||
    <Entry key="LockScreen_ApplicationsTerminateOption">
 | 
			
		||||
        强制关闭防作弊考试专用浏览器。警告:将无法保存数据或执行任何进一步的操作,关闭操作将立即启动。
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorContinueOption">
 | 
			
		||||
        暂时允许光标配置。这仅适用于当前运行的会话!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorMessage">
 | 
			
		||||
        已检测到禁止的光标配置。要解锁 SEB,请选择一个可用选项并输入正确的解锁密码。
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_CursorTerminateOption">
 | 
			
		||||
        终止安全考试浏览器。警告:将无法保存数据或执行任何其他操作,系统将立即关闭!
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="LockScreen_DisplayConfigurationContinueOption">
 | 
			
		||||
        暂时允许显示配置。 这仅适用于当前正在运行的会话!
 | 
			
		||||
    </Entry>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,16 +54,18 @@ namespace SafeExamBrowser.Runtime
 | 
			
		|||
			const int THIRTY_SECONDS = 30000;
 | 
			
		||||
 | 
			
		||||
			logger = new Logger();
 | 
			
		||||
			systemInfo = new SystemInfo();
 | 
			
		||||
 | 
			
		||||
			InitializeConfiguration();
 | 
			
		||||
			InitializeLogging();
 | 
			
		||||
			InitializeText();
 | 
			
		||||
 | 
			
		||||
			var nativeMethods = new NativeMethods();
 | 
			
		||||
			var registry = new Registry(ModuleLogger(nameof(Registry)));
 | 
			
		||||
			var uiFactory = new UserInterfaceFactory(text);
 | 
			
		||||
			var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
 | 
			
		||||
 | 
			
		||||
			systemInfo = new SystemInfo(registry);
 | 
			
		||||
 | 
			
		||||
			var args = Environment.GetCommandLineArgs();
 | 
			
		||||
			var integrityModule = new IntegrityModule(appConfig, ModuleLogger(nameof(IntegrityModule)));
 | 
			
		||||
			var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory)));
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +77,6 @@ namespace SafeExamBrowser.Runtime
 | 
			
		|||
			var messageBox = new MessageBoxFactory(text);
 | 
			
		||||
			var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
 | 
			
		||||
			var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), ModuleLogger(nameof(ProxyFactory)));
 | 
			
		||||
			var registry = new Registry(ModuleLogger(nameof(Registry)));
 | 
			
		||||
			var remoteSessionDetector = new RemoteSessionDetector(ModuleLogger(nameof(RemoteSessionDetector)));
 | 
			
		||||
			var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), ModuleLogger(nameof(RuntimeHost)), FIVE_SECONDS);
 | 
			
		||||
			var runtimeWindow = uiFactory.CreateRuntimeWindow(appConfig);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,8 @@
 | 
			
		|||
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using SafeExamBrowser.Core.Contracts.OperationModel;
 | 
			
		||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
| 
						 | 
				
			
			@ -32,14 +34,28 @@ namespace SafeExamBrowser.Runtime.Operations
 | 
			
		|||
		{
 | 
			
		||||
			StatusChanged?.Invoke(TextKey.OperationStatus_VerifySessionIntegrity);
 | 
			
		||||
 | 
			
		||||
			return VerifyEaseOfAccessConfiguration();
 | 
			
		||||
			var success = VerifyCursorConfiguration();
 | 
			
		||||
 | 
			
		||||
			if (success)
 | 
			
		||||
			{
 | 
			
		||||
				success = VerifyEaseOfAccessConfiguration();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return success ? OperationResult.Success : OperationResult.Failed;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public override OperationResult Repeat()
 | 
			
		||||
		{
 | 
			
		||||
			StatusChanged?.Invoke(TextKey.OperationStatus_VerifySessionIntegrity);
 | 
			
		||||
 | 
			
		||||
			return VerifyEaseOfAccessConfiguration();
 | 
			
		||||
			var success = VerifyCursorConfiguration();
 | 
			
		||||
 | 
			
		||||
			if (success)
 | 
			
		||||
			{
 | 
			
		||||
				success = VerifyEaseOfAccessConfiguration();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return success ? OperationResult.Success : OperationResult.Failed;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public override OperationResult Revert()
 | 
			
		||||
| 
						 | 
				
			
			@ -47,9 +63,44 @@ namespace SafeExamBrowser.Runtime.Operations
 | 
			
		|||
			return OperationResult.Success;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private OperationResult VerifyEaseOfAccessConfiguration()
 | 
			
		||||
		private bool VerifyCursorConfiguration()
 | 
			
		||||
		{
 | 
			
		||||
			var result = OperationResult.Failed;
 | 
			
		||||
			var success = true;
 | 
			
		||||
			var systemPath = $@"{Environment.ExpandEnvironmentVariables("%SystemRoot%")}\cursors\";
 | 
			
		||||
 | 
			
		||||
			logger.Info($"Attempting to verify cursor configuration...");
 | 
			
		||||
 | 
			
		||||
			if (registry.TryGetNames(RegistryValue.UserHive.Cursors_Key, out var cursors))
 | 
			
		||||
			{
 | 
			
		||||
				foreach (var cursor in cursors.Where(c => !string.IsNullOrWhiteSpace(c)))
 | 
			
		||||
				{
 | 
			
		||||
					success &= registry.TryRead(RegistryValue.UserHive.Cursors_Key, cursor, out var value);
 | 
			
		||||
					success &= value == default || !(value is string) || (value is string path && (string.IsNullOrWhiteSpace(path) || path.StartsWith(systemPath)));
 | 
			
		||||
 | 
			
		||||
					if (!success)
 | 
			
		||||
					{
 | 
			
		||||
						logger.Warn($"{(value != default ? $"Cursor configuration is compromised: '{value}'" : $"Failed to verify configuration of cursor '{cursor}'")}! Aborting session initialization...");
 | 
			
		||||
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (success)
 | 
			
		||||
				{
 | 
			
		||||
					logger.Info("Cursor configuration successfully verified.");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				logger.Error("Failed to verify cursor configuration!");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return success;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool VerifyEaseOfAccessConfiguration()
 | 
			
		||||
		{
 | 
			
		||||
			var success = false;
 | 
			
		||||
 | 
			
		||||
			logger.Info($"Attempting to verify ease of access configuration...");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -57,12 +108,12 @@ namespace SafeExamBrowser.Runtime.Operations
 | 
			
		|||
			{
 | 
			
		||||
				if (value == default || (value is string s && string.IsNullOrWhiteSpace(s)))
 | 
			
		||||
				{
 | 
			
		||||
					result = OperationResult.Success;
 | 
			
		||||
					success = true;
 | 
			
		||||
					logger.Info("Ease of access configuration successfully verified.");
 | 
			
		||||
				}
 | 
			
		||||
				else if (!Context.Next.Settings.Service.IgnoreService)
 | 
			
		||||
				{
 | 
			
		||||
					result = OperationResult.Success;
 | 
			
		||||
					success = true;
 | 
			
		||||
					logger.Info($"Ease of access configuration is compromised ('{value}'), but service will be active in the next session.");
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +126,7 @@ namespace SafeExamBrowser.Runtime.Operations
 | 
			
		|||
				logger.Error("Failed to verify ease of access configuration!");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return result;
 | 
			
		||||
			return success;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,9 @@
 | 
			
		|||
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.SystemComponents.Contracts
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
| 
						 | 
				
			
			@ -62,5 +65,10 @@ namespace SafeExamBrowser.SystemComponents.Contracts
 | 
			
		|||
		/// Provides the device ID information of the user's Plug and Play devices.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		string[] PlugAndPlayDeviceIds { get; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Retrieves all logical drives of the computer system.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		IEnumerable<DriveInfo> GetDrives();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,5 +11,5 @@ namespace SafeExamBrowser.SystemComponents.Contracts.Registry.Events
 | 
			
		|||
	/// <summary>
 | 
			
		||||
	/// Indicates that a registry value has changed.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public delegate void RegistryValueChangedEventHandler(object oldValue, object newValue);
 | 
			
		||||
	public delegate void RegistryValueChangedEventHandler(string key, string name, object oldValue, object newValue);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,8 +6,8 @@
 | 
			
		|||
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts.Registry.Events;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts.Registry.Events;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.SystemComponents.Contracts.Registry
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +31,11 @@ namespace SafeExamBrowser.SystemComponents.Contracts.Registry
 | 
			
		|||
		/// </summary>
 | 
			
		||||
		void StopMonitoring();
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Stops the monitoring of the specified registry value.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		void StopMonitoring(string key, string name);
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Attempts to read the value of the given name under the specified registry key.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,35 @@ namespace SafeExamBrowser.SystemComponents.Contracts.Registry
 | 
			
		|||
			public const string AppPaths_Key = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
 | 
			
		||||
			public const string EaseOfAccess_Key = @"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Utilman.exe";
 | 
			
		||||
			public const string EaseOfAccess_Name = "Debugger";
 | 
			
		||||
			public const string HardwareConfig_Key = @"HKEY_LOCAL_MACHINE\SYSTEM\HardwareConfig";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// All registry values located in the user hive.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public static class UserHive
 | 
			
		||||
		{
 | 
			
		||||
			public const string Cursors_Key = @"HKEY_CURRENT_USER\Control Panel\Cursors";
 | 
			
		||||
			public const string Cursors_AppStarting_Name = "AppStarting";
 | 
			
		||||
			public const string Cursors_Arrow_Name = "Arrow";
 | 
			
		||||
			public const string Cursors_Crosshair_Name = "Crosshair";
 | 
			
		||||
			public const string Cursors_Hand_Name = "Hand";
 | 
			
		||||
			public const string Cursors_Help_Name = "Help";
 | 
			
		||||
			public const string Cursors_IBeam_Name = "IBeam";
 | 
			
		||||
			public const string Cursors_No_Name = "No";
 | 
			
		||||
			public const string Cursors_NWPen_Name = "NWPen";
 | 
			
		||||
			public const string Cursors_Person_Name = "Person";
 | 
			
		||||
			public const string Cursors_Pin_Name = "Pin";
 | 
			
		||||
			public const string Cursors_SizeAll_Name = "SizeAll";
 | 
			
		||||
			public const string Cursors_SizeNESW_Name = "SizeNESW";
 | 
			
		||||
			public const string Cursors_SizeNS_Name = "SizeNS";
 | 
			
		||||
			public const string Cursors_SizeNWSE_Name = "SizeNWSE";
 | 
			
		||||
			public const string Cursors_SizeWE_Name = "SizeWE";
 | 
			
		||||
			public const string Cursors_UpArrow_Name = "UpArrow";
 | 
			
		||||
			public const string Cursors_Wait_Name = "Wait";
 | 
			
		||||
			public const string DeviceCache_Key = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\TaskFlow\DeviceCache";
 | 
			
		||||
			public const string NoDrives_Key = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer";
 | 
			
		||||
			public const string NoDrives_Name = "NoDrives";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,6 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
using System.Timers;
 | 
			
		||||
using Microsoft.Win32;
 | 
			
		||||
using SafeExamBrowser.Logging.Contracts;
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +20,7 @@ namespace SafeExamBrowser.SystemComponents.Registry
 | 
			
		|||
	public class Registry : IRegistry
 | 
			
		||||
	{
 | 
			
		||||
		private readonly ILogger logger;
 | 
			
		||||
		private readonly ConcurrentBag<(string key, string name, object value)> values;
 | 
			
		||||
		private readonly ConcurrentDictionary<(string key, string name), object> values;
 | 
			
		||||
 | 
			
		||||
		private Timer timer;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +29,7 @@ namespace SafeExamBrowser.SystemComponents.Registry
 | 
			
		|||
		public Registry(ILogger logger)
 | 
			
		||||
		{
 | 
			
		||||
			this.logger = logger;
 | 
			
		||||
			this.values = new ConcurrentBag<(string key, string name, object value)>();
 | 
			
		||||
			this.values = new ConcurrentDictionary<(string key, string name), object>();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void StartMonitoring(string key, string name)
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +46,7 @@ namespace SafeExamBrowser.SystemComponents.Registry
 | 
			
		|||
 | 
			
		||||
			if (TryRead(key, name, out var value))
 | 
			
		||||
			{
 | 
			
		||||
				values.Add((key, name, value));
 | 
			
		||||
				values.TryAdd((key, name), value);
 | 
			
		||||
				logger.Debug($"Started monitoring value '{name}' from registry key '{key}'. Initial value: '{value}'.");
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
| 
						 | 
				
			
			@ -58,10 +57,7 @@ namespace SafeExamBrowser.SystemComponents.Registry
 | 
			
		|||
 | 
			
		||||
		public void StopMonitoring()
 | 
			
		||||
		{
 | 
			
		||||
			while (!values.IsEmpty)
 | 
			
		||||
			{
 | 
			
		||||
				values.TryTake(out _);
 | 
			
		||||
			}
 | 
			
		||||
			values.Clear();
 | 
			
		||||
 | 
			
		||||
			if (timer != null)
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -70,19 +66,24 @@ namespace SafeExamBrowser.SystemComponents.Registry
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void StopMonitoring(string key, string name)
 | 
			
		||||
		{
 | 
			
		||||
			values.TryRemove((key, name), out _);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public bool TryRead(string key, string name, out object value)
 | 
			
		||||
		{
 | 
			
		||||
			var success = true;
 | 
			
		||||
			var success = false;
 | 
			
		||||
 | 
			
		||||
			value = default;
 | 
			
		||||
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				value = Microsoft.Win32.Registry.GetValue(key, name, default);
 | 
			
		||||
				success = true;
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception e)
 | 
			
		||||
			{
 | 
			
		||||
				success = false;
 | 
			
		||||
				logger.Error($"Failed to read value '{name}' from registry key '{key}'!", e);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -93,12 +94,8 @@ namespace SafeExamBrowser.SystemComponents.Registry
 | 
			
		|||
		{
 | 
			
		||||
			names = default;
 | 
			
		||||
 | 
			
		||||
			if (!TryOpenKey(keyName, out var key))
 | 
			
		||||
			if (TryOpenKey(keyName, out var key))
 | 
			
		||||
			{
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var success = true;
 | 
			
		||||
				using (key)
 | 
			
		||||
				{
 | 
			
		||||
					try
 | 
			
		||||
| 
						 | 
				
			
			@ -107,25 +104,24 @@ namespace SafeExamBrowser.SystemComponents.Registry
 | 
			
		|||
					}
 | 
			
		||||
					catch (Exception e)
 | 
			
		||||
					{
 | 
			
		||||
					logger.Error($"Failed to get registry value names '{keyName}'!", e);
 | 
			
		||||
					success = false;
 | 
			
		||||
						logger.Error($"Failed to get registry value names for '{keyName}'!", e);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				logger.Warn($"Failed to get names for '{keyName}'.");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return success;
 | 
			
		||||
			return names != default;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public bool TryGetSubKeys(string keyName, out IEnumerable<string> subKeys)
 | 
			
		||||
		{
 | 
			
		||||
			subKeys = default;
 | 
			
		||||
 | 
			
		||||
			if (!TryOpenKey(keyName, out var key))
 | 
			
		||||
			if (TryOpenKey(keyName, out var key))
 | 
			
		||||
			{
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var success = true;
 | 
			
		||||
				using (key)
 | 
			
		||||
				{
 | 
			
		||||
					try
 | 
			
		||||
| 
						 | 
				
			
			@ -134,114 +130,97 @@ namespace SafeExamBrowser.SystemComponents.Registry
 | 
			
		|||
					}
 | 
			
		||||
					catch (Exception e)
 | 
			
		||||
					{
 | 
			
		||||
					logger.Error($"Failed to get registry value names '{keyName}'!", e);
 | 
			
		||||
					success = false;
 | 
			
		||||
						logger.Error($"Failed to get registry sub key names for '{keyName}'!", e);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				logger.Warn($"Failed to get sub keys for '{keyName}'.");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return success;
 | 
			
		||||
			return subKeys != default;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void Timer_Elapsed(object sender, ElapsedEventArgs e)
 | 
			
		||||
		{
 | 
			
		||||
			foreach (var item in values)
 | 
			
		||||
			{
 | 
			
		||||
				if (TryRead(item.key, item.name, out var value))
 | 
			
		||||
				if (TryRead(item.Key.key, item.Key.name, out var value))
 | 
			
		||||
				{
 | 
			
		||||
					if (item.value != value)
 | 
			
		||||
					if (!Equals(item.Value, value))
 | 
			
		||||
					{
 | 
			
		||||
						logger.Debug($"Value '{item.name}' from registry key '{item.key}' has changed from '{item.value}' to '{value}'!");
 | 
			
		||||
						ValueChanged?.Invoke(item.value, value);
 | 
			
		||||
						logger.Debug($"Value '{item.Key.name}' from registry key '{item.Key.key}' has changed from '{item.Value}' to '{value}'!");
 | 
			
		||||
						ValueChanged?.Invoke(item.Key.key, item.Key.name, item.Value, value);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					logger.Error($"Failed to monitor value '{item.name}' from registry key '{item.key}'!");
 | 
			
		||||
					logger.Error($"Failed to monitor value '{item.Key.name}' from registry key '{item.Key.key}'!");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool TryGetBaseKeyFromKeyName(string keyName, out RegistryKey baseKey, out string subKeyName)
 | 
			
		||||
		{
 | 
			
		||||
			baseKey = default;
 | 
			
		||||
			subKeyName = default;
 | 
			
		||||
 | 
			
		||||
			string basekeyName;
 | 
			
		||||
			var baseKeyLength = keyName.IndexOf('\\');
 | 
			
		||||
			if (baseKeyLength != -1)
 | 
			
		||||
			{
 | 
			
		||||
				basekeyName = keyName.Substring(0, baseKeyLength).ToUpper(System.Globalization.CultureInfo.InvariantCulture);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				basekeyName = keyName.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch (basekeyName)
 | 
			
		||||
			{
 | 
			
		||||
				case "HKEY_CURRENT_USER":
 | 
			
		||||
				case "HKCU":
 | 
			
		||||
					baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64);
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_LOCAL_MACHINE":
 | 
			
		||||
				case "HKLM":
 | 
			
		||||
					baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_CLASSES_ROOT":
 | 
			
		||||
				case "HKCR":
 | 
			
		||||
					baseKey = RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Registry64);
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_USERS":
 | 
			
		||||
				case "HKU":
 | 
			
		||||
					baseKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Registry64);
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_PERFORMANCE_DATA":
 | 
			
		||||
				case "HKPD":
 | 
			
		||||
					baseKey = RegistryKey.OpenBaseKey(RegistryHive.PerformanceData, RegistryView.Registry64);
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_CURRENT_CONFIG":
 | 
			
		||||
				case "HKCC":
 | 
			
		||||
					baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentConfig, RegistryView.Registry64);
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_DYN_DATA":
 | 
			
		||||
				case "HKDD":
 | 
			
		||||
					baseKey = RegistryKey.OpenBaseKey(RegistryHive.DynData, RegistryView.Registry64);
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (baseKeyLength == -1 || baseKeyLength == keyName.Length)
 | 
			
		||||
			{
 | 
			
		||||
				subKeyName = string.Empty;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				subKeyName = keyName.Substring(baseKeyLength + 1, keyName.Length - baseKeyLength - 1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool TryOpenKey(string keyName, out RegistryKey key)
 | 
			
		||||
		{
 | 
			
		||||
			key = default;
 | 
			
		||||
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				if (TryGetBaseKeyFromKeyName(keyName, out var baseKey, out var subKey))
 | 
			
		||||
				if (TryGetHiveForKey(keyName, out var hive))
 | 
			
		||||
				{
 | 
			
		||||
					key = baseKey.OpenSubKey(subKey);
 | 
			
		||||
					if (keyName == hive.Name)
 | 
			
		||||
					{
 | 
			
		||||
						key = hive;
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
						key = hive.OpenSubKey(keyName.Replace($@"{hive.Name}\", ""));
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					logger.Warn($"Failed to get hive for key '{keyName}'!");
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception e)
 | 
			
		||||
			{
 | 
			
		||||
				logger.Error($"Failed to open registry key '{keyName}'!", e);
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return key != default;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool TryGetHiveForKey(string keyName, out RegistryKey hive)
 | 
			
		||||
		{
 | 
			
		||||
			var length = keyName.IndexOf('\\');
 | 
			
		||||
			var name = length != -1 ? keyName.Substring(0, length).ToUpperInvariant() : keyName.ToUpperInvariant();
 | 
			
		||||
 | 
			
		||||
			hive = default;
 | 
			
		||||
 | 
			
		||||
			switch (name)
 | 
			
		||||
			{
 | 
			
		||||
				case "HKEY_CLASSES_ROOT":
 | 
			
		||||
					hive = Microsoft.Win32.Registry.ClassesRoot;
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_CURRENT_CONFIG":
 | 
			
		||||
					hive = Microsoft.Win32.Registry.CurrentConfig;
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_CURRENT_USER":
 | 
			
		||||
					hive = Microsoft.Win32.Registry.CurrentUser;
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_LOCAL_MACHINE":
 | 
			
		||||
					hive = Microsoft.Win32.Registry.LocalMachine;
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_PERFORMANCE_DATA":
 | 
			
		||||
					hive = Microsoft.Win32.Registry.PerformanceData;
 | 
			
		||||
					break;
 | 
			
		||||
				case "HKEY_USERS":
 | 
			
		||||
					hive = Microsoft.Win32.Registry.Users;
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return hive != default;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,10 +8,12 @@
 | 
			
		|||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Management;
 | 
			
		||||
using System.Windows.Forms;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
 | 
			
		||||
using BatteryChargeStatus = System.Windows.Forms.BatteryChargeStatus;
 | 
			
		||||
using OperatingSystem = SafeExamBrowser.SystemComponents.Contracts.OperatingSystem;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +21,8 @@ namespace SafeExamBrowser.SystemComponents
 | 
			
		|||
{
 | 
			
		||||
	public class SystemInfo : ISystemInfo
 | 
			
		||||
	{
 | 
			
		||||
		private readonly IRegistry registry;
 | 
			
		||||
 | 
			
		||||
		public string BiosInfo { get; private set; }
 | 
			
		||||
		public string CpuName { get; private set; }
 | 
			
		||||
		public bool HasBattery { get; private set; }
 | 
			
		||||
| 
						 | 
				
			
			@ -30,8 +34,10 @@ namespace SafeExamBrowser.SystemComponents
 | 
			
		|||
		public string OperatingSystemInfo => $"{OperatingSystemName()}, {Environment.OSVersion.VersionString} ({Architecture()})";
 | 
			
		||||
		public string[] PlugAndPlayDeviceIds { get; private set; }
 | 
			
		||||
 | 
			
		||||
		public SystemInfo()
 | 
			
		||||
		public SystemInfo(IRegistry registry)
 | 
			
		||||
		{
 | 
			
		||||
			this.registry = registry;
 | 
			
		||||
 | 
			
		||||
			InitializeBattery();
 | 
			
		||||
			InitializeBiosInfo();
 | 
			
		||||
			InitializeCpuName();
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +47,20 @@ namespace SafeExamBrowser.SystemComponents
 | 
			
		|||
			InitializePnPDevices();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IEnumerable<DriveInfo> GetDrives()
 | 
			
		||||
		{
 | 
			
		||||
			var drives = DriveInfo.GetDrives();
 | 
			
		||||
 | 
			
		||||
			registry.TryRead(RegistryValue.UserHive.NoDrives_Key, RegistryValue.UserHive.NoDrives_Name, out var value);
 | 
			
		||||
 | 
			
		||||
			if (value is int noDrives && noDrives > 0)
 | 
			
		||||
			{
 | 
			
		||||
				drives = drives.Where(drive => (noDrives & (int) Math.Pow(2, drive.RootDirectory.ToString()[0] - 65)) == 0).ToArray();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return drives;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void InitializeBattery()
 | 
			
		||||
		{
 | 
			
		||||
			var status = SystemInformation.PowerStatus.BatteryChargeStatus;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -136,16 +136,14 @@ namespace SafeExamBrowser.SystemComponents
 | 
			
		|||
 | 
			
		||||
		private bool HasHistoricVirtualMachineHardwareConfiguration()
 | 
			
		||||
		{
 | 
			
		||||
			const string HARDWARE_ROOT_KEY = @"HKEY_LOCAL_MACHINE\SYSTEM\HardwareConfig";
 | 
			
		||||
 | 
			
		||||
			var hasHistoricConfiguration = false;
 | 
			
		||||
 | 
			
		||||
			if (registry.TryGetSubKeys(HARDWARE_ROOT_KEY, out var hardwareConfigSubkeys))
 | 
			
		||||
			if (registry.TryGetSubKeys(RegistryValue.MachineHive.HardwareConfig_Key, out var hardwareConfigSubkeys))
 | 
			
		||||
			{
 | 
			
		||||
				foreach (var configId in hardwareConfigSubkeys)
 | 
			
		||||
				{
 | 
			
		||||
					var hardwareConfigKey = $"{HARDWARE_ROOT_KEY}\\{configId}";
 | 
			
		||||
					var computerIdsKey = $"{hardwareConfigKey}\\ComputerIds";
 | 
			
		||||
					var hardwareConfigKey = $@"{RegistryValue.MachineHive.HardwareConfig_Key}\{configId}";
 | 
			
		||||
					var computerIdsKey = $@"{hardwareConfigKey}\ComputerIds";
 | 
			
		||||
					var success = true;
 | 
			
		||||
 | 
			
		||||
					success &= registry.TryRead(hardwareConfigKey, "BIOSVendor", out var biosVendor);
 | 
			
		||||
| 
						 | 
				
			
			@ -178,17 +176,15 @@ namespace SafeExamBrowser.SystemComponents
 | 
			
		|||
 | 
			
		||||
		private bool HasLocalVirtualMachineDeviceCache()
 | 
			
		||||
		{
 | 
			
		||||
			const string DEVICE_CACHE_PARENT_KEY = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\TaskFlow\DeviceCache";
 | 
			
		||||
 | 
			
		||||
			var deviceName = System.Environment.GetEnvironmentVariable("COMPUTERNAME");
 | 
			
		||||
			var hasDeviceCache = false;
 | 
			
		||||
			var hasDeviceCacheKeys = registry.TryGetSubKeys(DEVICE_CACHE_PARENT_KEY, out var deviceCacheKeys);
 | 
			
		||||
			var hasDeviceCacheKeys = registry.TryGetSubKeys(RegistryValue.UserHive.DeviceCache_Key, out var deviceCacheKeys);
 | 
			
		||||
 | 
			
		||||
			if (deviceName != default && hasDeviceCacheKeys)
 | 
			
		||||
			{
 | 
			
		||||
				foreach (var cacheId in deviceCacheKeys)
 | 
			
		||||
				{
 | 
			
		||||
					var cacheIdKey = $@"{DEVICE_CACHE_PARENT_KEY}\{cacheId}";
 | 
			
		||||
					var cacheIdKey = $@"{RegistryValue.UserHive.DeviceCache_Key}\{cacheId}";
 | 
			
		||||
					var didReadKeys = true;
 | 
			
		||||
 | 
			
		||||
					didReadKeys &= registry.TryRead(cacheIdKey, "DeviceName", out var cacheDeviceName);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
 | 
			
		||||
using System.Windows;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Desktop.Windows;
 | 
			
		||||
| 
						 | 
				
			
			@ -16,10 +17,12 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
{
 | 
			
		||||
	public class FileSystemDialogFactory : IFileSystemDialog
 | 
			
		||||
	{
 | 
			
		||||
		private readonly ISystemInfo systemInfo;
 | 
			
		||||
		private readonly IText text;
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialogFactory(IText text)
 | 
			
		||||
		public FileSystemDialogFactory(ISystemInfo systemInfo, IText text)
 | 
			
		||||
		{
 | 
			
		||||
			this.systemInfo = systemInfo;
 | 
			
		||||
			this.text = text;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,11 +38,11 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
		{
 | 
			
		||||
			if (parent is Window window)
 | 
			
		||||
			{
 | 
			
		||||
				return window.Dispatcher.Invoke(() => new FileSystemDialog(element, operation, text, initialPath, message, title, parent, restrictNavigation, showElementPath).Show());
 | 
			
		||||
				return window.Dispatcher.Invoke(() => new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, parent, restrictNavigation, showElementPath).Show());
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				return new FileSystemDialog(element, operation, text, initialPath, message, title, restrictNavigation: restrictNavigation, showElementPath: showElementPath).Show();
 | 
			
		||||
				return new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, restrictNavigation: restrictNavigation, showElementPath: showElementPath).Show();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,8 +15,8 @@ using System.Windows.Controls;
 | 
			
		|||
using System.Windows.Input;
 | 
			
		||||
using System.Windows.Media;
 | 
			
		||||
using FontAwesome.WPF;
 | 
			
		||||
using Microsoft.Win32;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
 | 
			
		|||
		private readonly string message;
 | 
			
		||||
		private readonly FileSystemOperation operation;
 | 
			
		||||
		private readonly IWindow parent;
 | 
			
		||||
		private readonly ISystemInfo systemInfo;
 | 
			
		||||
		private readonly bool restrictNavigation;
 | 
			
		||||
		private readonly bool showElementPath;
 | 
			
		||||
		private readonly IText text;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
 | 
			
		|||
		internal FileSystemDialog(
 | 
			
		||||
			FileSystemElement element,
 | 
			
		||||
			FileSystemOperation operation,
 | 
			
		||||
			ISystemInfo systemInfo,
 | 
			
		||||
			IText text,
 | 
			
		||||
			string initialPath = default,
 | 
			
		||||
			string message = default,
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +52,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
 | 
			
		|||
			this.initialPath = initialPath;
 | 
			
		||||
			this.message = message;
 | 
			
		||||
			this.operation = operation;
 | 
			
		||||
			this.systemInfo = systemInfo;
 | 
			
		||||
			this.parent = parent;
 | 
			
		||||
			this.restrictNavigation = restrictNavigation;
 | 
			
		||||
			this.showElementPath = showElementPath;
 | 
			
		||||
| 
						 | 
				
			
			@ -293,20 +296,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
 | 
			
		|||
			InitializeFileSystem();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private DriveInfo[] GetDrives()
 | 
			
		||||
		{
 | 
			
		||||
			var drives = DriveInfo.GetDrives();
 | 
			
		||||
			var noDrives = Registry.GetValue("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", "NoDrives", 0) as int?;
 | 
			
		||||
 | 
			
		||||
			if (noDrives.HasValue && noDrives > 0)
 | 
			
		||||
			{
 | 
			
		||||
				return drives.Where(drive => (noDrives & (int) Math.Pow(2, drive.RootDirectory.ToString()[0] - 65)) == 0).ToArray();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return drives;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		private void InitializeFileSystem()
 | 
			
		||||
		{
 | 
			
		||||
			if (restrictNavigation && !string.IsNullOrEmpty(initialPath))
 | 
			
		||||
| 
						 | 
				
			
			@ -337,7 +326,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
 | 
			
		|||
 | 
			
		||||
		private void InitializeUnrestricted()
 | 
			
		||||
		{
 | 
			
		||||
			foreach (var drive in GetDrives())
 | 
			
		||||
			foreach (var drive in systemInfo.GetDrives())
 | 
			
		||||
			{
 | 
			
		||||
				FileSystem.Items.Add(CreateItem(drive.RootDirectory));
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
 | 
			
		||||
using System.Windows;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Mobile.Windows;
 | 
			
		||||
| 
						 | 
				
			
			@ -16,10 +17,12 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
{
 | 
			
		||||
	public class FileSystemDialogFactory : IFileSystemDialog
 | 
			
		||||
	{
 | 
			
		||||
		private readonly ISystemInfo systemInfo;
 | 
			
		||||
		private readonly IText text;
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialogFactory(IText text)
 | 
			
		||||
		public FileSystemDialogFactory(ISystemInfo systemInfo, IText text)
 | 
			
		||||
		{
 | 
			
		||||
			this.systemInfo = systemInfo;
 | 
			
		||||
			this.text = text;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,11 +38,11 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
		{
 | 
			
		||||
			if (parent is Window window)
 | 
			
		||||
			{
 | 
			
		||||
				return window.Dispatcher.Invoke(() => new FileSystemDialog(element, operation, text, initialPath, message, title, parent, restrictNavigation, showElementPath).Show());
 | 
			
		||||
				return window.Dispatcher.Invoke(() => new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, parent, restrictNavigation, showElementPath).Show());
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				return new FileSystemDialog(element, operation, text, initialPath, message, title, restrictNavigation: restrictNavigation, showElementPath: showElementPath).Show();
 | 
			
		||||
				return new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, restrictNavigation: restrictNavigation, showElementPath: showElementPath).Show();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,8 +15,8 @@ using System.Windows.Controls;
 | 
			
		|||
using System.Windows.Input;
 | 
			
		||||
using System.Windows.Media;
 | 
			
		||||
using FontAwesome.WPF;
 | 
			
		||||
using Microsoft.Win32;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
 | 
			
		|||
		private readonly string message;
 | 
			
		||||
		private readonly FileSystemOperation operation;
 | 
			
		||||
		private readonly IWindow parent;
 | 
			
		||||
		private readonly ISystemInfo systemInfo;
 | 
			
		||||
		private readonly bool restrictNavigation;
 | 
			
		||||
		private readonly bool showElementPath;
 | 
			
		||||
		private readonly IText text;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
 | 
			
		|||
		internal FileSystemDialog(
 | 
			
		||||
			FileSystemElement element,
 | 
			
		||||
			FileSystemOperation operation,
 | 
			
		||||
			ISystemInfo systemInfo,
 | 
			
		||||
			IText text,
 | 
			
		||||
			string initialPath = default,
 | 
			
		||||
			string message = default,
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +52,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
 | 
			
		|||
			this.initialPath = initialPath;
 | 
			
		||||
			this.message = message;
 | 
			
		||||
			this.operation = operation;
 | 
			
		||||
			this.systemInfo = systemInfo;
 | 
			
		||||
			this.parent = parent;
 | 
			
		||||
			this.restrictNavigation = restrictNavigation;
 | 
			
		||||
			this.showElementPath = showElementPath;
 | 
			
		||||
| 
						 | 
				
			
			@ -293,20 +296,6 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
 | 
			
		|||
			InitializeFileSystem();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private DriveInfo[] GetDrives()
 | 
			
		||||
		{
 | 
			
		||||
			var drives = DriveInfo.GetDrives();
 | 
			
		||||
			var noDrives = Registry.GetValue("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", "NoDrives", 0) as int?;
 | 
			
		||||
 | 
			
		||||
			if (noDrives.HasValue && noDrives > 0)
 | 
			
		||||
			{
 | 
			
		||||
				return drives.Where(drive => (noDrives & (int) Math.Pow(2, drive.RootDirectory.ToString()[0] - 65)) == 0).ToArray();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return drives;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		private void InitializeFileSystem()
 | 
			
		||||
		{
 | 
			
		||||
			if (restrictNavigation && !string.IsNullOrEmpty(initialPath))
 | 
			
		||||
| 
						 | 
				
			
			@ -337,7 +326,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
 | 
			
		|||
 | 
			
		||||
		private void InitializeUnrestricted()
 | 
			
		||||
		{
 | 
			
		||||
			foreach (var drive in GetDrives())
 | 
			
		||||
			foreach (var drive in systemInfo.GetDrives())
 | 
			
		||||
			{
 | 
			
		||||
				FileSystem.Items.Add(CreateItem(drive.RootDirectory));
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue