From 94f356b77aa400a9fba860dcaeedb29232466842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Mon, 24 Jul 2017 15:29:17 +0200 Subject: [PATCH] Implemented basic window handling. --- SafeExamBrowser.Contracts/I18n/Key.cs | 2 + .../UserInterface/IUiElementFactory.cs | 12 +-- .../Behaviour/ShutdownControllerTests.cs | 24 +---- .../Behaviour/StartupControllerTests.cs | 28 +----- .../BrowserInitializationOperation.cs | 2 +- .../Operations/ProcessMonitoringOperation.cs | 4 +- .../TaskbarInitializationOperation.cs | 2 +- .../Operations/WindowMonitoringOperation.cs | 48 +++++++++ .../Operations/WorkingAreaOperation.cs | 4 +- .../Behaviour/ShutdownController.cs | 24 +---- .../Behaviour/StartupController.cs | 30 +----- SafeExamBrowser.Core/I18n/Text.xml | 2 + .../SafeExamBrowser.Core.csproj | 1 + .../Windows/WindowMonitor.cs | 39 ++++++-- .../SafeExamBrowser.UserInterface.csproj | 1 - .../SplashScreen.xaml | 5 +- SafeExamBrowser.UserInterface/Taskbar.xaml | 46 ++++----- .../UiElementFactory.cs | 32 +++++- .../WpfMessageBox.cs | 43 -------- .../Constants/Constant.cs | 4 + .../Constants/ShowWindowCommand.cs | 97 +++++++++++++++++++ .../SafeExamBrowser.WindowsApi.csproj | 1 + SafeExamBrowser.WindowsApi/User32.cs | 84 ++++++++++++++-- SafeExamBrowser/CompositionRoot.cs | 7 +- 24 files changed, 346 insertions(+), 196 deletions(-) create mode 100644 SafeExamBrowser.Core/Behaviour/Operations/WindowMonitoringOperation.cs delete mode 100644 SafeExamBrowser.UserInterface/WpfMessageBox.cs create mode 100644 SafeExamBrowser.WindowsApi/Constants/ShowWindowCommand.cs diff --git a/SafeExamBrowser.Contracts/I18n/Key.cs b/SafeExamBrowser.Contracts/I18n/Key.cs index e8060f11..af6dbb2f 100644 --- a/SafeExamBrowser.Contracts/I18n/Key.cs +++ b/SafeExamBrowser.Contracts/I18n/Key.cs @@ -23,11 +23,13 @@ namespace SafeExamBrowser.Contracts.I18n SplashScreen_InitializeBrowser, SplashScreen_InitializeProcessMonitoring, SplashScreen_InitializeTaskbar, + SplashScreen_InitializeWindowMonitoring, SplashScreen_InitializeWorkingArea, SplashScreen_RestoreWorkingArea, SplashScreen_ShutdownProcedure, SplashScreen_StartupProcedure, SplashScreen_StopProcessMonitoring, + SplashScreen_StopWindowMonitoring, SplashScreen_WaitExplorerStartup, SplashScreen_WaitExplorerTermination, Version diff --git a/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs b/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs index 55fe0277..23e732e6 100644 --- a/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs +++ b/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs @@ -11,21 +11,21 @@ using SafeExamBrowser.Contracts.I18n; namespace SafeExamBrowser.Contracts.UserInterface { - public interface IUiElementFactory + public interface IUiElementFactory : IMessageBox { /// /// Creates a taskbar button, initialized with the given application information. /// ITaskbarButton CreateApplicationButton(IApplicationInfo info); - /// - /// Creates a new splash screen which runs on its own thread. - /// - ISplashScreen CreateSplashScreen(ISettings settings, IText text); - /// /// Creates a taskbar notification, initialized with the given notification information. /// ITaskbarNotification CreateNotification(INotificationInfo info); + + /// + /// Creates a new splash screen which runs on its own thread. + /// + ISplashScreen CreateSplashScreen(ISettings settings, IText text); } } diff --git a/SafeExamBrowser.Core.UnitTests/Behaviour/ShutdownControllerTests.cs b/SafeExamBrowser.Core.UnitTests/Behaviour/ShutdownControllerTests.cs index f2ad03f9..1af3cfea 100644 --- a/SafeExamBrowser.Core.UnitTests/Behaviour/ShutdownControllerTests.cs +++ b/SafeExamBrowser.Core.UnitTests/Behaviour/ShutdownControllerTests.cs @@ -13,7 +13,6 @@ using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.Logging; -using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Core.Behaviour; @@ -22,45 +21,24 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour [TestClass] public class ShutdownControllerTests { - private Mock browserControllerMock; - private Mock browserInfoMock; private Mock loggerMock; - private Mock messageBoxMock; - private Mock aboutInfoMock; - private Mock processMonitorMock; private Mock settingsMock; - private Mock taskbarMock; private Mock textMock; private Mock uiFactoryMock; - private Mock workingAreaMock; private IShutdownController sut; [TestInitialize] public void Initialize() { - browserControllerMock = new Mock(); - browserInfoMock = new Mock(); loggerMock = new Mock(); - messageBoxMock = new Mock(); - aboutInfoMock = new Mock(); - processMonitorMock = new Mock(); settingsMock = new Mock(); - taskbarMock = new Mock(); textMock = new Mock(); uiFactoryMock = new Mock(); - workingAreaMock = new Mock(); uiFactoryMock.Setup(f => f.CreateSplashScreen(settingsMock.Object, textMock.Object)).Returns(new Mock().Object); - sut = new ShutdownController( - loggerMock.Object, - messageBoxMock.Object, - processMonitorMock.Object, - settingsMock.Object, - textMock.Object, - uiFactoryMock.Object, - workingAreaMock.Object); + sut = new ShutdownController(loggerMock.Object, settingsMock.Object, textMock.Object, uiFactoryMock.Object); } [TestMethod] diff --git a/SafeExamBrowser.Core.UnitTests/Behaviour/StartupControllerTests.cs b/SafeExamBrowser.Core.UnitTests/Behaviour/StartupControllerTests.cs index 281a1996..389615d3 100644 --- a/SafeExamBrowser.Core.UnitTests/Behaviour/StartupControllerTests.cs +++ b/SafeExamBrowser.Core.UnitTests/Behaviour/StartupControllerTests.cs @@ -14,7 +14,6 @@ using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.Logging; -using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Core.Behaviour; @@ -23,49 +22,24 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour [TestClass] public class StartupControllerTests { - private Mock browserControllerMock; - private Mock browserInfoMock; private Mock loggerMock; - private Mock messageBoxMock; - private Mock aboutInfoMock; - private Mock processMonitorMock; private Mock settingsMock; - private Mock taskbarMock; private Mock textMock; private Mock uiFactoryMock; - private Mock workingAreaMock; private IStartupController sut; [TestInitialize] public void Initialize() { - browserControllerMock = new Mock(); - browserInfoMock = new Mock(); loggerMock = new Mock(); - messageBoxMock = new Mock(); - aboutInfoMock = new Mock(); - processMonitorMock = new Mock(); settingsMock = new Mock(); - taskbarMock = new Mock(); textMock = new Mock(); uiFactoryMock = new Mock(); - workingAreaMock = new Mock(); uiFactoryMock.Setup(f => f.CreateSplashScreen(settingsMock.Object, textMock.Object)).Returns(new Mock().Object); - sut = new StartupController( - browserControllerMock.Object, - browserInfoMock.Object, - loggerMock.Object, - messageBoxMock.Object, - aboutInfoMock.Object, - processMonitorMock.Object, - settingsMock.Object, - taskbarMock.Object, - textMock.Object, - uiFactoryMock.Object, - workingAreaMock.Object); + sut = new StartupController(loggerMock.Object, settingsMock.Object, textMock.Object, uiFactoryMock.Object); } [TestMethod] diff --git a/SafeExamBrowser.Core/Behaviour/Operations/BrowserInitializationOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/BrowserInitializationOperation.cs index c489ca08..0daf7abb 100644 --- a/SafeExamBrowser.Core/Behaviour/Operations/BrowserInitializationOperation.cs +++ b/SafeExamBrowser.Core/Behaviour/Operations/BrowserInitializationOperation.cs @@ -40,7 +40,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations public void Perform() { - logger.Info("--- Initializing browser ---"); + logger.Info("Initializing browser..."); SplashScreen.UpdateText(Key.SplashScreen_InitializeBrowser); var browserButton = uiFactory.CreateApplicationButton(browserInfo); diff --git a/SafeExamBrowser.Core/Behaviour/Operations/ProcessMonitoringOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/ProcessMonitoringOperation.cs index 862a947c..c60dc53e 100644 --- a/SafeExamBrowser.Core/Behaviour/Operations/ProcessMonitoringOperation.cs +++ b/SafeExamBrowser.Core/Behaviour/Operations/ProcessMonitoringOperation.cs @@ -29,7 +29,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations public void Perform() { - logger.Info("--- Initializing process monitoring ---"); + logger.Info("Initializing process monitoring..."); SplashScreen.UpdateText(Key.SplashScreen_WaitExplorerTermination, true); processMonitor.CloseExplorerShell(); @@ -42,7 +42,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations public void Revert() { - logger.Info("--- Stopping process monitoring ---"); + logger.Info("Stopping process monitoring..."); SplashScreen.UpdateText(Key.SplashScreen_StopProcessMonitoring); // TODO diff --git a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarInitializationOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarInitializationOperation.cs index 63552b82..758b7910 100644 --- a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarInitializationOperation.cs +++ b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarInitializationOperation.cs @@ -33,7 +33,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations public void Perform() { - logger.Info("--- Initializing taskbar ---"); + logger.Info("Initializing taskbar..."); SplashScreen.UpdateText(Key.SplashScreen_InitializeTaskbar); var aboutNotification = uiFactory.CreateNotification(aboutInfo); diff --git a/SafeExamBrowser.Core/Behaviour/Operations/WindowMonitoringOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/WindowMonitoringOperation.cs new file mode 100644 index 00000000..7b88f62e --- /dev/null +++ b/SafeExamBrowser.Core/Behaviour/Operations/WindowMonitoringOperation.cs @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using SafeExamBrowser.Contracts.Behaviour; +using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Contracts.Monitoring; +using SafeExamBrowser.Contracts.UserInterface; + +namespace SafeExamBrowser.Core.Behaviour.Operations +{ + public class WindowMonitoringOperation : IOperation + { + private ILogger logger; + private IWindowMonitor windowMonitor; + + public ISplashScreen SplashScreen { private get; set; } + + public WindowMonitoringOperation(ILogger logger, IWindowMonitor windowMonitor) + { + this.logger = logger; + this.windowMonitor = windowMonitor; + } + + public void Perform() + { + logger.Info("Initializing window monitoring..."); + SplashScreen.UpdateText(Key.SplashScreen_InitializeWindowMonitoring); + + windowMonitor.HideAllWindows(); + windowMonitor.StartMonitoringWindows(); + } + + public void Revert() + { + logger.Info("Stopping window monitoring..."); + SplashScreen.UpdateText(Key.SplashScreen_StopWindowMonitoring); + + windowMonitor.StopMonitoringWindows(); + windowMonitor.RestoreHiddenWindows(); + } + } +} diff --git a/SafeExamBrowser.Core/Behaviour/Operations/WorkingAreaOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/WorkingAreaOperation.cs index af911eb4..bdc642a4 100644 --- a/SafeExamBrowser.Core/Behaviour/Operations/WorkingAreaOperation.cs +++ b/SafeExamBrowser.Core/Behaviour/Operations/WorkingAreaOperation.cs @@ -31,7 +31,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations public void Perform() { - logger.Info("--- Initializing working area ---"); + logger.Info("Initializing working area..."); SplashScreen.UpdateText(Key.SplashScreen_InitializeWorkingArea); // TODO @@ -42,7 +42,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations public void Revert() { - logger.Info("--- Restoring working area ---"); + logger.Info("Restoring working area..."); SplashScreen.UpdateText(Key.SplashScreen_RestoreWorkingArea); // TODO diff --git a/SafeExamBrowser.Core/Behaviour/ShutdownController.cs b/SafeExamBrowser.Core/Behaviour/ShutdownController.cs index e238cfa0..b9e61e61 100644 --- a/SafeExamBrowser.Core/Behaviour/ShutdownController.cs +++ b/SafeExamBrowser.Core/Behaviour/ShutdownController.cs @@ -13,7 +13,6 @@ using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.Logging; -using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.UserInterface; namespace SafeExamBrowser.Core.Behaviour @@ -21,30 +20,17 @@ namespace SafeExamBrowser.Core.Behaviour public class ShutdownController : IShutdownController { private ILogger logger; - private IMessageBox messageBox; - private IProcessMonitor processMonitor; private ISettings settings; private ISplashScreen splashScreen; private IText text; private IUiElementFactory uiFactory; - private IWorkingArea workingArea; - public ShutdownController( - ILogger logger, - IMessageBox messageBox, - IProcessMonitor processMonitor, - ISettings settings, - IText text, - IUiElementFactory uiFactory, - IWorkingArea workingArea) + public ShutdownController(ILogger logger, ISettings settings, IText text, IUiElementFactory uiFactory) { this.logger = logger; - this.messageBox = messageBox; - this.processMonitor = processMonitor; this.settings = settings; this.text = text; this.uiFactory = uiFactory; - this.workingArea = workingArea; } public void FinalizeApplication(Queue operations) @@ -86,7 +72,7 @@ namespace SafeExamBrowser.Core.Behaviour private void LogAndShowException(Exception e) { logger.Error($"Failed to finalize application!", e); - messageBox.Show(text.Get(Key.MessageBox_ShutdownError), text.Get(Key.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error); + uiFactory.Show(text.Get(Key.MessageBox_ShutdownError), text.Get(Key.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error); } private void FinalizeApplicationLog(bool success = true) @@ -95,10 +81,8 @@ namespace SafeExamBrowser.Core.Behaviour { logger.Info("--- Application successfully finalized! ---"); } - else - { - logger.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); - } + + logger.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); } } } diff --git a/SafeExamBrowser.Core/Behaviour/StartupController.cs b/SafeExamBrowser.Core/Behaviour/StartupController.cs index be13b0c0..5111f4cd 100644 --- a/SafeExamBrowser.Core/Behaviour/StartupController.cs +++ b/SafeExamBrowser.Core/Behaviour/StartupController.cs @@ -14,52 +14,26 @@ using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.Logging; -using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.UserInterface; namespace SafeExamBrowser.Core.Behaviour { public class StartupController : IStartupController { - private IApplicationController browserController; - private IApplicationInfo browserInfo; private ILogger logger; - private IMessageBox messageBox; - private INotificationInfo aboutInfo; - private IProcessMonitor processMonitor; private ISettings settings; private ISplashScreen splashScreen; - private ITaskbar taskbar; private IText text; private IUiElementFactory uiFactory; - private IWorkingArea workingArea; private Stack stack = new Stack(); - public StartupController( - IApplicationController browserController, - IApplicationInfo browserInfo, - ILogger logger, - IMessageBox messageBox, - INotificationInfo aboutInfo, - IProcessMonitor processMonitor, - ISettings settings, - ITaskbar taskbar, - IText text, - IUiElementFactory uiFactory, - IWorkingArea workingArea) + public StartupController(ILogger logger, ISettings settings, IText text, IUiElementFactory uiFactory) { - this.browserController = browserController; - this.browserInfo = browserInfo; this.logger = logger; - this.messageBox = messageBox; - this.aboutInfo = aboutInfo; - this.processMonitor = processMonitor; this.settings = settings; - this.taskbar = taskbar; this.text = text; this.uiFactory = uiFactory; - this.workingArea = workingArea; } public bool TryInitializeApplication(Queue operations) @@ -138,7 +112,7 @@ namespace SafeExamBrowser.Core.Behaviour private void LogAndShowException(Exception e) { logger.Error($"Failed to initialize application!", e); - messageBox.Show(text.Get(Key.MessageBox_StartupError), text.Get(Key.MessageBox_StartupErrorTitle), icon: MessageBoxIcon.Error); + uiFactory.Show(text.Get(Key.MessageBox_StartupError), text.Get(Key.MessageBox_StartupErrorTitle), icon: MessageBoxIcon.Error); logger.Info("Reverting operations..."); } diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml index 4046706f..95c13c55 100644 --- a/SafeExamBrowser.Core/I18n/Text.xml +++ b/SafeExamBrowser.Core/I18n/Text.xml @@ -8,11 +8,13 @@ Initializing browser Initializing process monitoring Initializing taskbar + Initializing window monitoring Initializing working area Restoring working area Initiating shutdown procedure Initiating startup procedure Stopping process monitoring + Stopping window monitoring Waiting for Windows explorer to start up Waiting for Windows explorer to shut down Version diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj index ea76dd20..35af286c 100644 --- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj +++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj @@ -43,6 +43,7 @@ + diff --git a/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs b/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs index 86240d43..ac5df2a6 100644 --- a/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs +++ b/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs @@ -7,6 +7,7 @@ */ using System; +using System.Collections.Generic; using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.WindowsApi; @@ -16,17 +17,29 @@ namespace SafeExamBrowser.Monitoring.Windows public class WindowMonitor : IWindowMonitor { private ILogger logger; + private IList minimizedWindows = new List(); public WindowMonitor(ILogger logger) { this.logger = logger; - - // TODO: Make operation for window monitor OR operation for all desktop initialization?! - // ... } public void HideAllWindows() { + logger.Info("Saving windows to be minimized..."); + + foreach (var handle in User32.GetOpenWindows()) + { + var window = new Window + { + Handle = handle, + Title = User32.GetWindowTitle(handle) + }; + + minimizedWindows.Add(window); + logger.Info($"Saved window '{window.Title}' with handle = {window.Handle}."); + } + logger.Info("Minimizing all open windows..."); User32.MinimizeAllOpenWindows(); logger.Info("Open windows successfully minimized."); @@ -34,17 +47,31 @@ namespace SafeExamBrowser.Monitoring.Windows public void RestoreHiddenWindows() { - throw new NotImplementedException(); + logger.Info("Restoring all minimized windows..."); + + foreach (var window in minimizedWindows) + { + User32.RestoreWindow(window.Handle); + logger.Info($"Restored window '{window.Title}' with handle = {window.Handle}."); + } + + logger.Info("Minimized windows successfully restored."); } public void StartMonitoringWindows() { - throw new NotImplementedException(); + // TODO } public void StopMonitoringWindows() { - throw new NotImplementedException(); + // TODO + } + + private struct Window + { + internal IntPtr Handle { get; set; } + internal string Title { get; set; } } } } diff --git a/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj b/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj index 618e18ba..db842aad 100644 --- a/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj +++ b/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj @@ -85,7 +85,6 @@ - ResXFileCodeGenerator Resources.Designer.cs diff --git a/SafeExamBrowser.UserInterface/SplashScreen.xaml b/SafeExamBrowser.UserInterface/SplashScreen.xaml index eec07ee3..38e3c041 100644 --- a/SafeExamBrowser.UserInterface/SplashScreen.xaml +++ b/SafeExamBrowser.UserInterface/SplashScreen.xaml @@ -4,11 +4,12 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" - Title="SplashScreen" Height="200" Width="350" WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterScreen" Cursor="Wait" Icon="./Images/SafeExamBrowser.ico"> + Title="SplashScreen" Height="200" Width="350" WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterScreen" + Cursor="Wait" Icon="./Images/SafeExamBrowser.ico" ResizeMode="NoResize" Topmost="True"> - + diff --git a/SafeExamBrowser.UserInterface/Taskbar.xaml b/SafeExamBrowser.UserInterface/Taskbar.xaml index 64d5696a..08ef9925 100644 --- a/SafeExamBrowser.UserInterface/Taskbar.xaml +++ b/SafeExamBrowser.UserInterface/Taskbar.xaml @@ -10,26 +10,28 @@ - - - - - - - - - - 5 - - - - - - 5 - - - - - - + + + + + + + + + + + 5 + + + + + + 5 + + + + + + + diff --git a/SafeExamBrowser.UserInterface/UiElementFactory.cs b/SafeExamBrowser.UserInterface/UiElementFactory.cs index 9956843c..fc0641e1 100644 --- a/SafeExamBrowser.UserInterface/UiElementFactory.cs +++ b/SafeExamBrowser.UserInterface/UiElementFactory.cs @@ -7,6 +7,7 @@ */ using System.Threading; +using System.Windows; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.UserInterface; @@ -21,6 +22,16 @@ namespace SafeExamBrowser.UserInterface return new ApplicationButton(info); } + public void Show(string message, string title, MessageBoxAction action = MessageBoxAction.Confirm, MessageBoxIcon icon = MessageBoxIcon.Information) + { + MessageBox.Show(message, title, ToButton(action), ToImage(icon)); + } + + public ITaskbarNotification CreateNotification(INotificationInfo info) + { + return new NotificationIcon(info); + } + public ISplashScreen CreateSplashScreen(ISettings settings, IText text) { SplashScreen splashScreen = null; @@ -46,9 +57,26 @@ namespace SafeExamBrowser.UserInterface return splashScreen; } - public ITaskbarNotification CreateNotification(INotificationInfo info) + private MessageBoxButton ToButton(MessageBoxAction action) { - return new NotificationIcon(info); + switch (action) + { + default: + return MessageBoxButton.OK; + } + } + + private MessageBoxImage ToImage(MessageBoxIcon icon) + { + switch (icon) + { + case MessageBoxIcon.Warning: + return MessageBoxImage.Warning; + case MessageBoxIcon.Error: + return MessageBoxImage.Error; + default: + return MessageBoxImage.Information; + } } } } diff --git a/SafeExamBrowser.UserInterface/WpfMessageBox.cs b/SafeExamBrowser.UserInterface/WpfMessageBox.cs deleted file mode 100644 index 64061903..00000000 --- a/SafeExamBrowser.UserInterface/WpfMessageBox.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -using System.Windows; -using SafeExamBrowser.Contracts.UserInterface; - -namespace SafeExamBrowser.UserInterface -{ - public class WpfMessageBox : IMessageBox - { - public void Show(string message, string title, MessageBoxAction action = MessageBoxAction.Confirm, MessageBoxIcon icon = MessageBoxIcon.Information) - { - MessageBox.Show(message, title, ToButton(action), ToImage(icon)); - } - - private MessageBoxButton ToButton(MessageBoxAction action) - { - switch (action) - { - default: - return MessageBoxButton.OK; - } - } - - private MessageBoxImage ToImage(MessageBoxIcon icon) - { - switch (icon) - { - case MessageBoxIcon.Warning: - return MessageBoxImage.Warning; - case MessageBoxIcon.Error: - return MessageBoxImage.Error; - default: - return MessageBoxImage.Information; - } - } - } -} diff --git a/SafeExamBrowser.WindowsApi/Constants/Constant.cs b/SafeExamBrowser.WindowsApi/Constants/Constant.cs index 1cbb46d0..61af4425 100644 --- a/SafeExamBrowser.WindowsApi/Constants/Constant.cs +++ b/SafeExamBrowser.WindowsApi/Constants/Constant.cs @@ -11,6 +11,10 @@ namespace SafeExamBrowser.WindowsApi.Constants static class Constant { internal const int WM_COMMAND = 0x111; + + /// + /// Minimize all open windows. + /// internal const int MIN_ALL = 419; } } diff --git a/SafeExamBrowser.WindowsApi/Constants/ShowWindowCommand.cs b/SafeExamBrowser.WindowsApi/Constants/ShowWindowCommand.cs new file mode 100644 index 00000000..7d236995 --- /dev/null +++ b/SafeExamBrowser.WindowsApi/Constants/ShowWindowCommand.cs @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +namespace SafeExamBrowser.WindowsApi.Constants +{ + /// + /// See http://www.pinvoke.net/default.aspx/Enums/ShowWindowCommand.html. + /// + internal enum ShowWindowCommand + { + /// + /// Hides the window and activates another window. + /// + Hide = 0, + + /// + /// Activates and displays a window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when displaying the window + /// for the first time. + /// + Normal = 1, + + /// + /// Activates the window and displays it as a minimized window. + /// + ShowMinimized = 2, + + /// + /// Maximizes the specified window. + /// + Maximize = 3, + + /// + /// Activates the window and displays it as a maximized window. + /// + ShowMaximized = 3, + + /// + /// Displays a window in its most recent size and position. This value + /// is similar to , except + /// the window is not activated. + /// + ShowNoActivate = 4, + + /// + /// Activates the window and displays it in its current size and position. + /// + Show = 5, + + /// + /// Minimizes the specified window and activates the next top-level + /// window in the Z order. + /// + Minimize = 6, + + /// + /// Displays the window as a minimized window. This value is similar to + /// , except the + /// window is not activated. + /// + ShowMinNoActive = 7, + + /// + /// Displays the window in its current size and position. This value is + /// similar to , except the + /// window is not activated. + /// + ShowNA = 8, + + /// + /// Activates and displays the window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when restoring a minimized window. + /// + Restore = 9, + + /// + /// Sets the show state based on the SW_* value specified in the + /// STARTUPINFO structure passed to the CreateProcess function by the + /// program that started the application. + /// + ShowDefault = 10, + + /// + /// Windows 2000/XP: Minimizes a window, even if the thread + /// that owns the window is not responding. This flag should only be + /// used when minimizing windows from a different thread. + /// + ForceMinimize = 11 + } +} diff --git a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj index 074f5027..5e244155 100644 --- a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj +++ b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj @@ -41,6 +41,7 @@ + diff --git a/SafeExamBrowser.WindowsApi/User32.cs b/SafeExamBrowser.WindowsApi/User32.cs index 926f12d6..b5562b3a 100644 --- a/SafeExamBrowser.WindowsApi/User32.cs +++ b/SafeExamBrowser.WindowsApi/User32.cs @@ -7,8 +7,10 @@ */ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; +using System.Text; using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Types; @@ -19,6 +21,30 @@ namespace SafeExamBrowser.WindowsApi /// public static class User32 { + /// + /// Retrieves a collection of handles to all currently open (i.e. visible) windows. + /// + public static IEnumerable GetOpenWindows() + { + var windows = new List(); + var success = EnumWindows(delegate (IntPtr hWnd, IntPtr lParam) + { + if (hWnd != GetShellWindowHandle() && IsWindowVisible(hWnd) && GetWindowTextLength(hWnd) > 0) + { + windows.Add(hWnd); + } + + return true; + }, IntPtr.Zero); + + if (!success) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + return windows; + } + /// /// Retrieves a window handle to the Windows taskbar. Returns IntPtr.Zero /// if the taskbar could not be found (i.e. if it isn't running). @@ -32,7 +58,6 @@ namespace SafeExamBrowser.WindowsApi /// Retrieves the process ID of the main Windows explorer instance controlling /// desktop and taskbar or 0, if the process isn't running. /// - /// public static uint GetShellProcessId() { var handle = GetShellWindowHandle(); @@ -41,6 +66,26 @@ namespace SafeExamBrowser.WindowsApi return processId; } + /// + /// Retrieves the title of the specified window, or an empty string, if the + /// given window does not have a title. + /// + public static string GetWindowTitle(IntPtr window) + { + var length = GetWindowTextLength(window); + + if (length > 0) + { + var builder = new StringBuilder(length); + + GetWindowText(window, builder, length + 1); + + return builder.ToString(); + } + + return string.Empty; + } + /// /// Retrieves the currently configured working area of the primary screen. /// @@ -91,6 +136,14 @@ namespace SafeExamBrowser.WindowsApi } } + /// + /// Restores the specified window to its original size and position. + /// + public static void RestoreWindow(IntPtr window) + { + ShowWindow(window, (int) ShowWindowCommand.Restore); + } + /// /// Sets the working area of the primary screen according to the given dimensions. /// @@ -107,21 +160,40 @@ namespace SafeExamBrowser.WindowsApi } } + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); + [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount); + + [DllImport("user32.dll", SetLastError = true)] + private static extern int GetWindowTextLength(IntPtr hWnd); + [DllImport("user32.dll", SetLastError = true)] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); - - [DllImport("user32.dll", EntryPoint = "SendMessage")] - private static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + private static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref RECT pvParam, SPIF fWinIni); + private static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SendMessage")] + private static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", SetLastError = true)] + private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref RECT pvParam, SPIF fWinIni); } } diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs index 0a54ae71..46300259 100644 --- a/SafeExamBrowser/CompositionRoot.cs +++ b/SafeExamBrowser/CompositionRoot.cs @@ -30,7 +30,6 @@ namespace SafeExamBrowser private IApplicationController browserController; private IApplicationInfo browserInfo; private ILogger logger; - private IMessageBox messageBox; private INotificationInfo aboutInfo; private IProcessMonitor processMonitor; private ISettings settings; @@ -50,7 +49,6 @@ namespace SafeExamBrowser browserController = new BrowserApplicationController(); browserInfo = new BrowserApplicationInfo(); logger = new Logger(); - messageBox = new WpfMessageBox(); settings = new Settings(); Taskbar = new Taskbar(); textResource = new XmlTextResource(); @@ -63,10 +61,11 @@ namespace SafeExamBrowser processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor))); windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor))); workingArea = new WorkingArea(new ModuleLogger(logger, typeof(WorkingArea))); - ShutdownController = new ShutdownController(logger, messageBox, processMonitor, settings, text, uiFactory, workingArea); - StartupController = new StartupController(browserController, browserInfo, logger, messageBox, aboutInfo, processMonitor, settings, Taskbar, text, uiFactory, workingArea); + ShutdownController = new ShutdownController(logger, settings, text, uiFactory); + StartupController = new StartupController(logger, settings, text, uiFactory); StartupOperations = new Queue(); + StartupOperations.Enqueue(new WindowMonitoringOperation(logger, windowMonitor)); StartupOperations.Enqueue(new ProcessMonitoringOperation(logger, processMonitor)); StartupOperations.Enqueue(new WorkingAreaOperation(logger, Taskbar, workingArea)); StartupOperations.Enqueue(new TaskbarInitializationOperation(logger, aboutInfo, Taskbar, uiFactory));