SEBWIN-221: Moved hide & restore windows mechanism for Disable Explorer Shell to IExplorerShell and made reconfiguration dialogs modal to respective browser window.
This commit is contained in:
		
							parent
							
								
									bae7ed8a25
								
							
						
					
					
						commit
						b4f468a2b4
					
				
					 16 changed files with 142 additions and 68 deletions
				
			
		| 
						 | 
					@ -63,7 +63,7 @@ namespace SafeExamBrowser.Browser
 | 
				
			||||||
			var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} {Id}");
 | 
								var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} {Id}");
 | 
				
			||||||
			var downloadHandler = new DownloadHandler(appConfig, settings, downloadLogger);
 | 
								var downloadHandler = new DownloadHandler(appConfig, settings, downloadLogger);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			downloadHandler.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
 | 
								downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			control = new BrowserControl(appConfig, settings, controlLogger, text);
 | 
								control = new BrowserControl(appConfig, settings, controlLogger, text);
 | 
				
			||||||
			control.AddressChanged += Control_AddressChanged;
 | 
								control.AddressChanged += Control_AddressChanged;
 | 
				
			||||||
| 
						 | 
					@ -101,6 +101,13 @@ namespace SafeExamBrowser.Browser
 | 
				
			||||||
			NameChanged?.Invoke(title);
 | 
								NameChanged?.Invoke(title);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private void DownloadHandler_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								args.BrowserWindow = window;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ConfigurationDownloadRequested?.Invoke(fileName, args);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private void Window_AddressChanged(string address)
 | 
							private void Window_AddressChanged(string address)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			logger.Debug($"The user requested to navigate to '{address}'.");
 | 
								logger.Debug($"The user requested to navigate to '{address}'.");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,6 +82,7 @@ namespace SafeExamBrowser.Browser.Handlers
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			var args = new DownloadEventArgs();
 | 
								var args = new DownloadEventArgs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								logger.Debug($"Detected download request for configuration file '{downloadItem.Url}'.");
 | 
				
			||||||
			ConfigurationDownloadRequested?.Invoke(downloadItem.SuggestedFileName, args);
 | 
								ConfigurationDownloadRequested?.Invoke(downloadItem.SuggestedFileName, args);
 | 
				
			||||||
			logger.Debug($"Download of configuration file '{downloadItem.Url}' was {(args.AllowDownload ? "granted" : "denied")}.");
 | 
								logger.Debug($"Download of configuration file '{downloadItem.Url}' was {(args.AllowDownload ? "granted" : "denied")}.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			sut.Perform();
 | 
								sut.Perform();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			windowMonitorMock.Verify(w => w.HideAllWindows(), Times.Never);
 | 
					 | 
				
			||||||
			windowMonitorMock.Verify(w => w.StartMonitoringWindows(), Times.Once);
 | 
								windowMonitorMock.Verify(w => w.StartMonitoringWindows(), Times.Once);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,7 +46,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
 | 
				
			||||||
			sut.Revert();
 | 
								sut.Revert();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			windowMonitorMock.Verify(w => w.StopMonitoringWindows(), Times.Once);
 | 
								windowMonitorMock.Verify(w => w.StopMonitoringWindows(), Times.Once);
 | 
				
			||||||
			windowMonitorMock.Verify(w => w.RestoreHiddenWindows(), Times.Never);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
| 
						 | 
					@ -57,7 +55,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			sut.Perform();
 | 
								sut.Perform();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			windowMonitorMock.Verify(w => w.HideAllWindows(), Times.Never);
 | 
					 | 
				
			||||||
			windowMonitorMock.Verify(w => w.StartMonitoringWindows(), Times.Once);
 | 
								windowMonitorMock.Verify(w => w.StartMonitoringWindows(), Times.Once);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,7 +66,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
 | 
				
			||||||
			sut.Revert();
 | 
								sut.Revert();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			windowMonitorMock.Verify(w => w.StopMonitoringWindows(), Times.Once);
 | 
								windowMonitorMock.Verify(w => w.StopMonitoringWindows(), Times.Once);
 | 
				
			||||||
			windowMonitorMock.Verify(w => w.RestoreHiddenWindows(), Times.Never);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -206,9 +206,11 @@ namespace SafeExamBrowser.Client
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
 | 
								if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				logger.Info($"Detected download request for configuration file '{fileName}'.");
 | 
									logger.Debug($"Received download request for configuration file '{fileName}'. Asking user to confirm the reconfiguration...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				var result = messageBox.Show(TextKey.MessageBox_ReconfigurationQuestion, TextKey.MessageBox_ReconfigurationQuestionTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question);
 | 
									var message = TextKey.MessageBox_ReconfigurationQuestion;
 | 
				
			||||||
 | 
									var title = TextKey.MessageBox_ReconfigurationQuestionTitle;
 | 
				
			||||||
 | 
									var result = messageBox.Show(message, title, MessageBoxAction.YesNo, MessageBoxIcon.Question, args.BrowserWindow);
 | 
				
			||||||
				var reconfigure = result == MessageBoxResult.Yes;
 | 
									var reconfigure = result == MessageBoxResult.Yes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				logger.Info($"The user chose to {(reconfigure ? "start" : "abort")} the reconfiguration.");
 | 
									logger.Info($"The user chose to {(reconfigure ? "start" : "abort")} the reconfiguration.");
 | 
				
			||||||
| 
						 | 
					@ -223,7 +225,7 @@ namespace SafeExamBrowser.Client
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				logger.Info($"Denied download request for configuration file '{fileName}' due to '{Settings.ConfigurationMode}' mode.");
 | 
									logger.Info($"Denied download request for configuration file '{fileName}' due to '{Settings.ConfigurationMode}' mode.");
 | 
				
			||||||
				messageBox.Show(TextKey.MessageBox_ReconfigurationDenied, TextKey.MessageBox_ReconfigurationDeniedTitle);
 | 
									messageBox.Show(TextKey.MessageBox_ReconfigurationDenied, TextKey.MessageBox_ReconfigurationDeniedTitle, parent: args.BrowserWindow);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,12 @@
 | 
				
			||||||
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
					 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using SafeExamBrowser.Contracts.UserInterface.Browser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace SafeExamBrowser.Contracts.Browser
 | 
					namespace SafeExamBrowser.Contracts.Browser
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/// <summary>
 | 
						/// <summary>
 | 
				
			||||||
	/// The event arguments used for all download events fired by the <see cref="IBrowserApplicationController"/>.
 | 
						/// The event arguments used for all download events.
 | 
				
			||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	public class DownloadEventArgs
 | 
						public class DownloadEventArgs
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
| 
						 | 
					@ -18,6 +20,11 @@ namespace SafeExamBrowser.Contracts.Browser
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public bool AllowDownload { get; set; }
 | 
							public bool AllowDownload { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The browser window from which the download request originated.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public IBrowserWindow BrowserWindow { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Callback executed once a download has been finished.
 | 
							/// Callback executed once a download has been finished.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,16 +31,6 @@ namespace SafeExamBrowser.Contracts.Monitoring
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		bool Hide(IntPtr window);
 | 
							bool Hide(IntPtr window);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// Hides all currently opened windows.
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		void HideAllWindows();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// Restores all windows which were hidden during the startup procedure.
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		void RestoreHiddenWindows();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Starts monitoring application windows by subscribing to specific system events.
 | 
							/// Starts monitoring application windows by subscribing to specific system events.
 | 
				
			||||||
		/// If a window is shown which is not supposed to do so, it will be automatically hidden.
 | 
							/// If a window is shown which is not supposed to do so, it will be automatically hidden.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,16 @@ namespace SafeExamBrowser.Contracts.WindowsApi
 | 
				
			||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	public interface IExplorerShell
 | 
						public interface IExplorerShell
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Hides all currently opened windows. The explorer shell needs to be running in order to execute this operation!
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							void HideAllWindows();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Restores all previously hidden windows. The explorer shell needs to be running in order to execute this operation!
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							void RestoreAllWindows();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Resumes the explorer shell process, if it was previously suspended.
 | 
							/// Resumes the explorer shell process, if it was previously suspended.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,6 @@
 | 
				
			||||||
    <Compile Include="Mouse\MouseInterceptor.cs" />
 | 
					    <Compile Include="Mouse\MouseInterceptor.cs" />
 | 
				
			||||||
    <Compile Include="Processes\ProcessMonitor.cs" />
 | 
					    <Compile Include="Processes\ProcessMonitor.cs" />
 | 
				
			||||||
    <Compile Include="Properties\AssemblyInfo.cs" />
 | 
					    <Compile Include="Properties\AssemblyInfo.cs" />
 | 
				
			||||||
    <Compile Include="Windows\Window.cs" />
 | 
					 | 
				
			||||||
    <Compile Include="Windows\WindowMonitor.cs" />
 | 
					    <Compile Include="Windows\WindowMonitor.cs" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,6 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using SafeExamBrowser.Contracts.Logging;
 | 
					using SafeExamBrowser.Contracts.Logging;
 | 
				
			||||||
using SafeExamBrowser.Contracts.Monitoring;
 | 
					using SafeExamBrowser.Contracts.Monitoring;
 | 
				
			||||||
using SafeExamBrowser.Contracts.Monitoring.Events;
 | 
					using SafeExamBrowser.Contracts.Monitoring.Events;
 | 
				
			||||||
| 
						 | 
					@ -21,7 +20,6 @@ namespace SafeExamBrowser.Monitoring.Windows
 | 
				
			||||||
		private Guid? captureHookId;
 | 
							private Guid? captureHookId;
 | 
				
			||||||
		private Guid? foregroundHookId;
 | 
							private Guid? foregroundHookId;
 | 
				
			||||||
		private ILogger logger;
 | 
							private ILogger logger;
 | 
				
			||||||
		private IList<Window> minimizedWindows = new List<Window>();
 | 
					 | 
				
			||||||
		private INativeMethods nativeMethods;
 | 
							private INativeMethods nativeMethods;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public event WindowChangedEventHandler WindowChanged;
 | 
							public event WindowChangedEventHandler WindowChanged;
 | 
				
			||||||
| 
						 | 
					@ -57,40 +55,6 @@ namespace SafeExamBrowser.Monitoring.Windows
 | 
				
			||||||
			return success;
 | 
								return success;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public void HideAllWindows()
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			logger.Info("Searching for windows to be minimized...");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			foreach (var handle in nativeMethods.GetOpenWindows())
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				var window = new Window
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					Handle = handle,
 | 
					 | 
				
			||||||
					Title = nativeMethods.GetWindowTitle(handle)
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				minimizedWindows.Add(window);
 | 
					 | 
				
			||||||
				logger.Info($"Found window '{window.Title}' with handle = {window.Handle}.");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			logger.Info("Minimizing all open windows...");
 | 
					 | 
				
			||||||
			nativeMethods.MinimizeAllOpenWindows();
 | 
					 | 
				
			||||||
			logger.Info("Open windows successfully minimized.");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		public void RestoreHiddenWindows()
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			logger.Info("Restoring all minimized windows...");
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			foreach (var window in minimizedWindows)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				nativeMethods.RestoreWindow(window.Handle);
 | 
					 | 
				
			||||||
				logger.Info($"Restored window '{window.Title}' with handle = {window.Handle}.");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			logger.Info("Minimized windows successfully restored.");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		public void StartMonitoringWindows()
 | 
							public void StartMonitoringWindows()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			captureHookId = nativeMethods.RegisterSystemCaptureStartEvent(OnWindowChanged);
 | 
								captureHookId = nativeMethods.RegisterSystemCaptureStartEvent(OnWindowChanged);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,6 +80,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
			newDesktop.Verify(d => d.Activate(), Times.Once);
 | 
								newDesktop.Verify(d => d.Activate(), Times.Once);
 | 
				
			||||||
			processFactory.VerifySet(f => f.StartupDesktop = newDesktop.Object, Times.Once);
 | 
								processFactory.VerifySet(f => f.StartupDesktop = newDesktop.Object, Times.Once);
 | 
				
			||||||
			explorerShell.Verify(s => s.Suspend(), Times.Once);
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Once);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Never);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Assert.AreSame(sessionContext.NewDesktop, newDesktop.Object);
 | 
								Assert.AreSame(sessionContext.NewDesktop, newDesktop.Object);
 | 
				
			||||||
			Assert.AreSame(sessionContext.OriginalDesktop, originalDesktop.Object);
 | 
								Assert.AreSame(sessionContext.OriginalDesktop, originalDesktop.Object);
 | 
				
			||||||
| 
						 | 
					@ -93,10 +95,15 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
		public void MustCorrectlyInitializeDisableExplorerShell()
 | 
							public void MustCorrectlyInitializeDisableExplorerShell()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 | 
								var order = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			nextSettings.KioskMode = KioskMode.DisableExplorerShell;
 | 
								nextSettings.KioskMode = KioskMode.DisableExplorerShell;
 | 
				
			||||||
 | 
								explorerShell.Setup(s => s.HideAllWindows()).Callback(() => Assert.AreEqual(1, ++order));
 | 
				
			||||||
 | 
								explorerShell.Setup(s => s.Terminate()).Callback(() => Assert.AreEqual(2, ++order));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			sut.Perform();
 | 
								sut.Perform();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Once);
 | 
				
			||||||
			explorerShell.Verify(s => s.Terminate(), Times.Once);
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Once);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,6 +135,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
			processFactory.VerifySet(f => f.StartupDesktop = originalDesktop.Object, Times.Once);
 | 
								processFactory.VerifySet(f => f.StartupDesktop = originalDesktop.Object, Times.Once);
 | 
				
			||||||
			newDesktop.Verify(d => d.Close(), Times.Once);
 | 
								newDesktop.Verify(d => d.Close(), Times.Once);
 | 
				
			||||||
			explorerShell.Verify(s => s.Resume(), Times.Once);
 | 
								explorerShell.Verify(s => s.Resume(), Times.Once);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Start(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Never);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Assert.AreEqual(OperationResult.Success, performResult);
 | 
								Assert.AreEqual(OperationResult.Success, performResult);
 | 
				
			||||||
			Assert.AreEqual(OperationResult.Success, revertResult);
 | 
								Assert.AreEqual(OperationResult.Success, revertResult);
 | 
				
			||||||
| 
						 | 
					@ -140,13 +149,18 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
		public void MustCorrectlyRevertDisableExplorerShell()
 | 
							public void MustCorrectlyRevertDisableExplorerShell()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 | 
								var order = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			currentSettings.KioskMode = KioskMode.DisableExplorerShell;
 | 
								currentSettings.KioskMode = KioskMode.DisableExplorerShell;
 | 
				
			||||||
			nextSettings.KioskMode = KioskMode.DisableExplorerShell;
 | 
								nextSettings.KioskMode = KioskMode.DisableExplorerShell;
 | 
				
			||||||
 | 
								explorerShell.Setup(s => s.Start()).Callback(() => Assert.AreEqual(1, ++order));
 | 
				
			||||||
 | 
								explorerShell.Setup(s => s.RestoreAllWindows()).Callback(() => Assert.AreEqual(2, ++order));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			var performResult = sut.Perform();
 | 
								var performResult = sut.Perform();
 | 
				
			||||||
			var revertResult = sut.Revert();
 | 
								var revertResult = sut.Revert();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			explorerShell.Verify(s => s.Start(), Times.Once);
 | 
								explorerShell.Verify(s => s.Start(), Times.Once);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Once);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Assert.AreEqual(OperationResult.Success, performResult);
 | 
								Assert.AreEqual(OperationResult.Success, performResult);
 | 
				
			||||||
			Assert.AreEqual(OperationResult.Success, revertResult);
 | 
								Assert.AreEqual(OperationResult.Success, revertResult);
 | 
				
			||||||
| 
						 | 
					@ -171,6 +185,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
			explorerShell.Verify(s => s.Start(), Times.Never);
 | 
								explorerShell.Verify(s => s.Start(), Times.Never);
 | 
				
			||||||
			explorerShell.Verify(s => s.Resume(), Times.Never);
 | 
								explorerShell.Verify(s => s.Resume(), Times.Never);
 | 
				
			||||||
			explorerShell.Verify(s => s.Suspend(), Times.Once);
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Once);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Never);
 | 
				
			||||||
			newDesktop.Verify(d => d.Activate(), Times.Once);
 | 
								newDesktop.Verify(d => d.Activate(), Times.Once);
 | 
				
			||||||
			newDesktop.Verify(d => d.Close(), Times.Never);
 | 
								newDesktop.Verify(d => d.Close(), Times.Never);
 | 
				
			||||||
			originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
								originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
| 
						 | 
					@ -181,10 +197,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Assert.AreEqual(OperationResult.Success, result);
 | 
								Assert.AreEqual(OperationResult.Success, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			explorerShell.Verify(s => s.Resume(), Times.Never);
 | 
					 | 
				
			||||||
			explorerShell.Verify(s => s.Terminate(), Times.Once);
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Once);
 | 
				
			||||||
			explorerShell.Verify(s => s.Suspend(), Times.Once);
 | 
					 | 
				
			||||||
			explorerShell.Verify(s => s.Start(), Times.Never);
 | 
								explorerShell.Verify(s => s.Start(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Resume(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Once);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Once);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Never);
 | 
				
			||||||
			newDesktop.Verify(d => d.Activate(), Times.Once);
 | 
								newDesktop.Verify(d => d.Activate(), Times.Once);
 | 
				
			||||||
			newDesktop.Verify(d => d.Close(), Times.Never);
 | 
								newDesktop.Verify(d => d.Close(), Times.Never);
 | 
				
			||||||
			originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
								originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
| 
						 | 
					@ -196,10 +214,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Assert.AreEqual(OperationResult.Success, result);
 | 
								Assert.AreEqual(OperationResult.Success, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			explorerShell.Verify(s => s.Resume(), Times.Never);
 | 
					 | 
				
			||||||
			explorerShell.Verify(s => s.Terminate(), Times.Once);
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Once);
 | 
				
			||||||
			explorerShell.Verify(s => s.Suspend(), Times.Exactly(2));
 | 
					 | 
				
			||||||
			explorerShell.Verify(s => s.Start(), Times.Never);
 | 
								explorerShell.Verify(s => s.Start(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Resume(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Exactly(2));
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Once);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Never);
 | 
				
			||||||
			newDesktop.Verify(d => d.Activate(), Times.Exactly(2));
 | 
								newDesktop.Verify(d => d.Activate(), Times.Exactly(2));
 | 
				
			||||||
			newDesktop.Verify(d => d.Close(), Times.Never);
 | 
								newDesktop.Verify(d => d.Close(), Times.Never);
 | 
				
			||||||
			originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
								originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,6 +88,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
			explorerShell.Verify(s => s.Start(), Times.Once);
 | 
								explorerShell.Verify(s => s.Start(), Times.Once);
 | 
				
			||||||
			explorerShell.Verify(s => s.Suspend(), Times.Never);
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Never);
 | 
				
			||||||
			explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Once);
 | 
				
			||||||
			newDesktop.Verify(d => d.Activate(), Times.Never);
 | 
								newDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
			newDesktop.Verify(d => d.Close(), Times.Never);
 | 
								newDesktop.Verify(d => d.Close(), Times.Never);
 | 
				
			||||||
			originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
								originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
| 
						 | 
					@ -103,6 +105,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
			explorerShell.Verify(s => s.Start(), Times.Once);
 | 
								explorerShell.Verify(s => s.Start(), Times.Once);
 | 
				
			||||||
			explorerShell.Verify(s => s.Suspend(), Times.Never);
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Never);
 | 
				
			||||||
			explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Once);
 | 
				
			||||||
			newDesktop.Verify(d => d.Activate(), Times.Never);
 | 
								newDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
			newDesktop.Verify(d => d.Close(), Times.Once);
 | 
								newDesktop.Verify(d => d.Close(), Times.Once);
 | 
				
			||||||
			originalDesktop.Verify(d => d.Activate(), Times.Once);
 | 
								originalDesktop.Verify(d => d.Activate(), Times.Once);
 | 
				
			||||||
| 
						 | 
					@ -118,6 +122,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
			explorerShell.Verify(s => s.Start(), Times.Exactly(2));
 | 
								explorerShell.Verify(s => s.Start(), Times.Exactly(2));
 | 
				
			||||||
			explorerShell.Verify(s => s.Suspend(), Times.Never);
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Never);
 | 
				
			||||||
			explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Exactly(2));
 | 
				
			||||||
			newDesktop.Verify(d => d.Activate(), Times.Never);
 | 
								newDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
			newDesktop.Verify(d => d.Close(), Times.Once);
 | 
								newDesktop.Verify(d => d.Close(), Times.Once);
 | 
				
			||||||
			originalDesktop.Verify(d => d.Activate(), Times.Once);
 | 
								originalDesktop.Verify(d => d.Activate(), Times.Once);
 | 
				
			||||||
| 
						 | 
					@ -126,7 +132,45 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
		public void MustNotTerminateKioskModeIfSameInNextSesssion()
 | 
							public void MustNotTerminateKioskModeIfSameInNextSesssion()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 | 
								var newDesktop = new Mock<IDesktop>();
 | 
				
			||||||
 | 
								var originalDesktop = new Mock<IDesktop>();
 | 
				
			||||||
 | 
								var result = default(OperationResult);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sessionContext.NewDesktop = newDesktop.Object;
 | 
				
			||||||
 | 
								sessionContext.OriginalDesktop = originalDesktop.Object;
 | 
				
			||||||
 | 
								sessionContext.ActiveMode = KioskMode.DisableExplorerShell;
 | 
				
			||||||
 | 
								nextSettings.KioskMode = KioskMode.DisableExplorerShell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result = sut.Repeat();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Assert.AreEqual(OperationResult.Success, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Resume(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Start(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Never);
 | 
				
			||||||
 | 
								newDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
 | 
								newDesktop.Verify(d => d.Close(), Times.Never);
 | 
				
			||||||
 | 
								originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sessionContext.ActiveMode = KioskMode.CreateNewDesktop;
 | 
				
			||||||
 | 
								nextSettings.KioskMode = KioskMode.CreateNewDesktop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result = sut.Repeat();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Assert.AreEqual(OperationResult.Success, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Resume(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Start(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Suspend(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.Terminate(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.HideAllWindows(), Times.Never);
 | 
				
			||||||
 | 
								explorerShell.Verify(s => s.RestoreAllWindows(), Times.Never);
 | 
				
			||||||
 | 
								newDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
 | 
								newDesktop.Verify(d => d.Close(), Times.Never);
 | 
				
			||||||
 | 
								originalDesktop.Verify(d => d.Activate(), Times.Never);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		[TestMethod]
 | 
							[TestMethod]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -158,17 +158,16 @@ namespace SafeExamBrowser.Runtime.Operations
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			StatusChanged?.Invoke(TextKey.OperationStatus_WaitExplorerTermination);
 | 
								StatusChanged?.Invoke(TextKey.OperationStatus_WaitExplorerTermination);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// TODO: Hiding all windows must be done here, as the explorer shell is needed to do so!
 | 
								explorerShell.HideAllWindows();
 | 
				
			||||||
 | 
					 | 
				
			||||||
			explorerShell.Terminate();
 | 
								explorerShell.Terminate();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private void RestartExplorerShell()
 | 
							private void RestartExplorerShell()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			StatusChanged?.Invoke(TextKey.OperationStatus_WaitExplorerStartup);
 | 
								StatusChanged?.Invoke(TextKey.OperationStatus_WaitExplorerStartup);
 | 
				
			||||||
			explorerShell.Start();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// TODO: Restore all hidden windows!
 | 
								explorerShell.Start();
 | 
				
			||||||
 | 
								explorerShell.RestoreAllWindows();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -448,9 +448,6 @@ namespace SafeExamBrowser.Runtime
 | 
				
			||||||
			runtimeWindow?.UpdateStatus(status, true);
 | 
								runtimeWindow?.UpdateStatus(status, true);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// TODO: Move to utility in core library and use in client controller!
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		private void MapProgress(IProgressIndicator progressIndicator, ProgressChangedEventArgs args)
 | 
							private void MapProgress(IProgressIndicator progressIndicator, ProgressChangedEventArgs args)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (args.CurrentValue.HasValue)
 | 
								if (args.CurrentValue.HasValue)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ using System.Linq;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
using SafeExamBrowser.Contracts.Logging;
 | 
					using SafeExamBrowser.Contracts.Logging;
 | 
				
			||||||
using SafeExamBrowser.Contracts.WindowsApi;
 | 
					using SafeExamBrowser.Contracts.WindowsApi;
 | 
				
			||||||
 | 
					using SafeExamBrowser.WindowsApi.Types;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace SafeExamBrowser.WindowsApi
 | 
					namespace SafeExamBrowser.WindowsApi
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -21,15 +22,51 @@ namespace SafeExamBrowser.WindowsApi
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private ILogger logger;
 | 
							private ILogger logger;
 | 
				
			||||||
		private INativeMethods nativeMethods;
 | 
							private INativeMethods nativeMethods;
 | 
				
			||||||
 | 
							private IList<Window> minimizedWindows = new List<Window>();
 | 
				
			||||||
		private IList<ProcessThread> suspendedThreads;
 | 
							private IList<ProcessThread> suspendedThreads;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public ExplorerShell(ILogger logger, INativeMethods nativeMethods)
 | 
							public ExplorerShell(ILogger logger, INativeMethods nativeMethods)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			this.logger = logger;
 | 
								this.logger = logger;
 | 
				
			||||||
			this.nativeMethods = nativeMethods;
 | 
								this.nativeMethods = nativeMethods;
 | 
				
			||||||
 | 
								this.minimizedWindows = new List<Window>();
 | 
				
			||||||
			this.suspendedThreads = new List<ProcessThread>();
 | 
								this.suspendedThreads = new List<ProcessThread>();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void HideAllWindows()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								logger.Info("Searching for windows to be minimized...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								foreach (var handle in nativeMethods.GetOpenWindows())
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									var window = new Window
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Handle = handle,
 | 
				
			||||||
 | 
										Title = nativeMethods.GetWindowTitle(handle)
 | 
				
			||||||
 | 
									};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									minimizedWindows.Add(window);
 | 
				
			||||||
 | 
									logger.Info($"Found window '{window.Title}' with handle = {window.Handle}.");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								logger.Info("Minimizing all open windows...");
 | 
				
			||||||
 | 
								nativeMethods.MinimizeAllOpenWindows();
 | 
				
			||||||
 | 
								logger.Info("Open windows successfully minimized.");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void RestoreAllWindows()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								logger.Info("Restoring all minimized windows...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								foreach (var window in minimizedWindows)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									nativeMethods.RestoreWindow(window.Handle);
 | 
				
			||||||
 | 
									logger.Info($"Restored window '{window.Title}' with handle = {window.Handle}.");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								logger.Info("Minimized windows successfully restored.");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public void Resume()
 | 
							public void Resume()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			const int MAX_ATTEMPTS = 3;
 | 
								const int MAX_ATTEMPTS = 3;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,6 +83,7 @@
 | 
				
			||||||
    <Compile Include="Types\PROCESS_INFORMATION.cs" />
 | 
					    <Compile Include="Types\PROCESS_INFORMATION.cs" />
 | 
				
			||||||
    <Compile Include="Types\RECT.cs" />
 | 
					    <Compile Include="Types\RECT.cs" />
 | 
				
			||||||
    <Compile Include="Types\STARTUPINFO.cs" />
 | 
					    <Compile Include="Types\STARTUPINFO.cs" />
 | 
				
			||||||
 | 
					    <Compile Include="Types\Window.cs" />
 | 
				
			||||||
    <Compile Include="User32.cs" />
 | 
					    <Compile Include="User32.cs" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace SafeExamBrowser.Monitoring.Windows
 | 
					namespace SafeExamBrowser.WindowsApi.Types
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	internal struct Window
 | 
						internal struct Window
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue