Implemented basic window handling.

This commit is contained in:
Damian Büchel 2017-07-24 15:29:17 +02:00
parent f5d76980d0
commit 94f356b77a
24 changed files with 346 additions and 196 deletions

View file

@ -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

View file

@ -11,21 +11,21 @@ using SafeExamBrowser.Contracts.I18n;
namespace SafeExamBrowser.Contracts.UserInterface
{
public interface IUiElementFactory
public interface IUiElementFactory : IMessageBox
{
/// <summary>
/// Creates a taskbar button, initialized with the given application information.
/// </summary>
ITaskbarButton CreateApplicationButton(IApplicationInfo info);
/// <summary>
/// Creates a new splash screen which runs on its own thread.
/// </summary>
ISplashScreen CreateSplashScreen(ISettings settings, IText text);
/// <summary>
/// Creates a taskbar notification, initialized with the given notification information.
/// </summary>
ITaskbarNotification CreateNotification(INotificationInfo info);
/// <summary>
/// Creates a new splash screen which runs on its own thread.
/// </summary>
ISplashScreen CreateSplashScreen(ISettings settings, IText text);
}
}

View file

@ -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<IApplicationController> browserControllerMock;
private Mock<IApplicationInfo> browserInfoMock;
private Mock<ILogger> loggerMock;
private Mock<IMessageBox> messageBoxMock;
private Mock<INotificationInfo> aboutInfoMock;
private Mock<IProcessMonitor> processMonitorMock;
private Mock<ISettings> settingsMock;
private Mock<ITaskbar> taskbarMock;
private Mock<IText> textMock;
private Mock<IUiElementFactory> uiFactoryMock;
private Mock<IWorkingArea> workingAreaMock;
private IShutdownController sut;
[TestInitialize]
public void Initialize()
{
browserControllerMock = new Mock<IApplicationController>();
browserInfoMock = new Mock<IApplicationInfo>();
loggerMock = new Mock<ILogger>();
messageBoxMock = new Mock<IMessageBox>();
aboutInfoMock = new Mock<INotificationInfo>();
processMonitorMock = new Mock<IProcessMonitor>();
settingsMock = new Mock<ISettings>();
taskbarMock = new Mock<ITaskbar>();
textMock = new Mock<IText>();
uiFactoryMock = new Mock<IUiElementFactory>();
workingAreaMock = new Mock<IWorkingArea>();
uiFactoryMock.Setup(f => f.CreateSplashScreen(settingsMock.Object, textMock.Object)).Returns(new Mock<ISplashScreen>().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]

View file

@ -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<IApplicationController> browserControllerMock;
private Mock<IApplicationInfo> browserInfoMock;
private Mock<ILogger> loggerMock;
private Mock<IMessageBox> messageBoxMock;
private Mock<INotificationInfo> aboutInfoMock;
private Mock<IProcessMonitor> processMonitorMock;
private Mock<ISettings> settingsMock;
private Mock<ITaskbar> taskbarMock;
private Mock<IText> textMock;
private Mock<IUiElementFactory> uiFactoryMock;
private Mock<IWorkingArea> workingAreaMock;
private IStartupController sut;
[TestInitialize]
public void Initialize()
{
browserControllerMock = new Mock<IApplicationController>();
browserInfoMock = new Mock<IApplicationInfo>();
loggerMock = new Mock<ILogger>();
messageBoxMock = new Mock<IMessageBox>();
aboutInfoMock = new Mock<INotificationInfo>();
processMonitorMock = new Mock<IProcessMonitor>();
settingsMock = new Mock<ISettings>();
taskbarMock = new Mock<ITaskbar>();
textMock = new Mock<IText>();
uiFactoryMock = new Mock<IUiElementFactory>();
workingAreaMock = new Mock<IWorkingArea>();
uiFactoryMock.Setup(f => f.CreateSplashScreen(settingsMock.Object, textMock.Object)).Returns(new Mock<ISplashScreen>().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]

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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();
}
}
}

View file

@ -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

View file

@ -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<IOperation> 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")}");
}
}
}

View file

@ -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<IOperation> stack = new Stack<IOperation>();
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<IOperation> 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...");
}

View file

@ -8,11 +8,13 @@
<SplashScreen_InitializeBrowser>Initializing browser</SplashScreen_InitializeBrowser>
<SplashScreen_InitializeProcessMonitoring>Initializing process monitoring</SplashScreen_InitializeProcessMonitoring>
<SplashScreen_InitializeTaskbar>Initializing taskbar</SplashScreen_InitializeTaskbar>
<SplashScreen_InitializeWindowMonitoring>Initializing window monitoring</SplashScreen_InitializeWindowMonitoring>
<SplashScreen_InitializeWorkingArea>Initializing working area</SplashScreen_InitializeWorkingArea>
<SplashScreen_RestoreWorkingArea>Restoring working area</SplashScreen_RestoreWorkingArea>
<SplashScreen_ShutdownProcedure>Initiating shutdown procedure</SplashScreen_ShutdownProcedure>
<SplashScreen_StartupProcedure>Initiating startup procedure</SplashScreen_StartupProcedure>
<SplashScreen_StopProcessMonitoring>Stopping process monitoring</SplashScreen_StopProcessMonitoring>
<SplashScreen_StopWindowMonitoring>Stopping window monitoring</SplashScreen_StopWindowMonitoring>
<SplashScreen_WaitExplorerStartup>Waiting for Windows explorer to start up</SplashScreen_WaitExplorerStartup>
<SplashScreen_WaitExplorerTermination>Waiting for Windows explorer to shut down</SplashScreen_WaitExplorerTermination>
<Version>Version</Version>

View file

@ -43,6 +43,7 @@
<Compile Include="Behaviour\Operations\BrowserInitializationOperation.cs" />
<Compile Include="Behaviour\Operations\ProcessMonitoringOperation.cs" />
<Compile Include="Behaviour\Operations\TaskbarInitializationOperation.cs" />
<Compile Include="Behaviour\Operations\WindowMonitoringOperation.cs" />
<Compile Include="Behaviour\Operations\WorkingAreaOperation.cs" />
<Compile Include="Behaviour\ShutdownController.cs" />
<Compile Include="Behaviour\StartupController.cs" />

View file

@ -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<Window> minimizedWindows = new List<Window>();
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; }
}
}
}

View file

@ -85,7 +85,6 @@
<Compile Include="Utilities\IconResourceLoader.cs" />
<Compile Include="ViewModels\DateTimeViewModel.cs" />
<Compile Include="ViewModels\SplashScreenViewModel.cs" />
<Compile Include="WpfMessageBox.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>

View file

@ -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">
<Window.Background>
<SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background>
<Border BorderBrush="CadetBlue" BorderThickness="1">
<Border BorderBrush="DodgerBlue" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />

View file

@ -10,26 +10,28 @@
<Window.Background>
<SolidColorBrush Color="Black" Opacity="0.8" />
</Window.Background>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" x:Name="ApplicationScrollViewer" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" PreviewMouseWheel="ApplicationScrollViewer_PreviewMouseWheel">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.HorizontalScrollBarHeightKey}">5</s:Double>
</ScrollViewer.Resources>
<StackPanel x:Name="ApplicationStackPanel" Orientation="Horizontal" />
</ScrollViewer>
<ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
</ScrollViewer.Resources>
<WrapPanel x:Name="NotificationWrapPanel" Margin="10,2,5,2" MaxWidth="100" VerticalAlignment="Center" />
</ScrollViewer>
<local:DateTimeControl Grid.Column="2" />
<local:QuitButton Grid.Column="3" />
</Grid>
<Border BorderBrush="White" BorderThickness="0,0.5,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" x:Name="ApplicationScrollViewer" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" PreviewMouseWheel="ApplicationScrollViewer_PreviewMouseWheel">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.HorizontalScrollBarHeightKey}">5</s:Double>
</ScrollViewer.Resources>
<StackPanel x:Name="ApplicationStackPanel" Orientation="Horizontal" />
</ScrollViewer>
<ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
</ScrollViewer.Resources>
<WrapPanel x:Name="NotificationWrapPanel" Margin="10,2,5,2" MaxWidth="100" VerticalAlignment="Center" />
</ScrollViewer>
<local:DateTimeControl Grid.Column="2" />
<local:QuitButton Grid.Column="3" />
</Grid>
</Border>
</Window>

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -11,6 +11,10 @@ namespace SafeExamBrowser.WindowsApi.Constants
static class Constant
{
internal const int WM_COMMAND = 0x111;
/// <summary>
/// Minimize all open windows.
/// </summary>
internal const int MIN_ALL = 419;
}
}

View file

@ -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
{
/// <remarks>
/// See http://www.pinvoke.net/default.aspx/Enums/ShowWindowCommand.html.
/// </remarks>
internal enum ShowWindowCommand
{
/// <summary>
/// Hides the window and activates another window.
/// </summary>
Hide = 0,
/// <summary>
/// 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.
/// </summary>
Normal = 1,
/// <summary>
/// Activates the window and displays it as a minimized window.
/// </summary>
ShowMinimized = 2,
/// <summary>
/// Maximizes the specified window.
/// </summary>
Maximize = 3,
/// <summary>
/// Activates the window and displays it as a maximized window.
/// </summary>
ShowMaximized = 3,
/// <summary>
/// Displays a window in its most recent size and position. This value
/// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except
/// the window is not activated.
/// </summary>
ShowNoActivate = 4,
/// <summary>
/// Activates the window and displays it in its current size and position.
/// </summary>
Show = 5,
/// <summary>
/// Minimizes the specified window and activates the next top-level
/// window in the Z order.
/// </summary>
Minimize = 6,
/// <summary>
/// Displays the window as a minimized window. This value is similar to
/// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the
/// window is not activated.
/// </summary>
ShowMinNoActive = 7,
/// <summary>
/// Displays the window in its current size and position. This value is
/// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the
/// window is not activated.
/// </summary>
ShowNA = 8,
/// <summary>
/// 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.
/// </summary>
Restore = 9,
/// <summary>
/// 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.
/// </summary>
ShowDefault = 10,
/// <summary>
/// <b>Windows 2000/XP:</b> 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.
/// </summary>
ForceMinimize = 11
}
}

View file

@ -41,6 +41,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Constants\Constant.cs" />
<Compile Include="Constants\ShowWindowCommand.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Constants\SPI.cs" />
<Compile Include="Constants\SPIF.cs" />

View file

@ -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
/// </summary>
public static class User32
{
/// <summary>
/// Retrieves a collection of handles to all currently open (i.e. visible) windows.
/// </summary>
public static IEnumerable<IntPtr> GetOpenWindows()
{
var windows = new List<IntPtr>();
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;
}
/// <summary>
/// Retrieves a window handle to the Windows taskbar. Returns <c>IntPtr.Zero</c>
/// 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 <c>0</c>, if the process isn't running.
/// </summary>
/// <returns></returns>
public static uint GetShellProcessId()
{
var handle = GetShellWindowHandle();
@ -41,6 +66,26 @@ namespace SafeExamBrowser.WindowsApi
return processId;
}
/// <summary>
/// Retrieves the title of the specified window, or an empty string, if the
/// given window does not have a title.
/// </summary>
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;
}
/// <summary>
/// Retrieves the currently configured working area of the primary screen.
/// </summary>
@ -91,6 +136,14 @@ namespace SafeExamBrowser.WindowsApi
}
}
/// <summary>
/// Restores the specified window to its original size and position.
/// </summary>
public static void RestoreWindow(IntPtr window)
{
ShowWindow(window, (int) ShowWindowCommand.Restore);
}
/// <summary>
/// Sets the working area of the primary screen according to the given dimensions.
/// </summary>
@ -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);
}
}

View file

@ -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<IOperation>();
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));