Implemented basic display / sleep mode handling and added logging to taskbar.
This commit is contained in:
parent
49540af39d
commit
939bc7f79a
28 changed files with 436 additions and 174 deletions
|
@ -10,7 +10,7 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration.Settings
|
namespace SafeExamBrowser.Configuration
|
||||||
{
|
{
|
||||||
public class BrowserSettings : IBrowserSettings
|
public class BrowserSettings : IBrowserSettings
|
||||||
{
|
{
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration.Settings
|
namespace SafeExamBrowser.Configuration
|
||||||
{
|
{
|
||||||
public class KeyboardSettings : IKeyboardSettings
|
public class KeyboardSettings : IKeyboardSettings
|
||||||
{
|
{
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration.Settings
|
namespace SafeExamBrowser.Configuration
|
||||||
{
|
{
|
||||||
public class MouseSettings : IMouseSettings
|
public class MouseSettings : IMouseSettings
|
||||||
{
|
{
|
||||||
public bool AllowMiddleButton => false;
|
public bool AllowMiddleButton => false;
|
||||||
|
|
||||||
public bool AllowRightButton => false;
|
public bool AllowRightButton => true;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -60,12 +60,10 @@
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Bounds.cs" />
|
<Compile Include="BrowserSettings.cs" />
|
||||||
<Compile Include="Settings\BrowserSettings.cs" />
|
<Compile Include="KeyboardSettings.cs" />
|
||||||
<Compile Include="Settings\KeyboardSettings.cs" />
|
<Compile Include="MouseSettings.cs" />
|
||||||
<Compile Include="Settings\MouseSettings.cs" />
|
<Compile Include="Settings.cs" />
|
||||||
<Compile Include="Settings\SettingsImpl.cs" />
|
|
||||||
<Compile Include="WorkingArea.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using SafeExamBrowser.Configuration.Settings;
|
|
||||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration
|
namespace SafeExamBrowser.Configuration
|
||||||
|
@ -17,11 +16,11 @@ namespace SafeExamBrowser.Configuration
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// TODO: Replace with proper implementation once configuration aspects are clear...
|
/// TODO: Replace with proper implementation once configuration aspects are clear...
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class SettingsImpl : ISettings
|
public class Settings : ISettings
|
||||||
{
|
{
|
||||||
private static readonly string LogFileDate = DateTime.Now.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s");
|
private static readonly string LogFileDate = DateTime.Now.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s");
|
||||||
|
|
||||||
public SettingsImpl()
|
public Settings()
|
||||||
{
|
{
|
||||||
Browser = new BrowserSettings(this);
|
Browser = new BrowserSettings(this);
|
||||||
Keyboard = new KeyboardSettings();
|
Keyboard = new KeyboardSettings();
|
|
@ -1,62 +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.Forms;
|
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
|
||||||
using SafeExamBrowser.Contracts.UserInterface;
|
|
||||||
using SafeExamBrowser.Contracts.WindowsApi;
|
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration
|
|
||||||
{
|
|
||||||
public class WorkingArea : IWorkingArea
|
|
||||||
{
|
|
||||||
private ILogger logger;
|
|
||||||
private INativeMethods nativeMethods;
|
|
||||||
private IBounds originalWorkingArea;
|
|
||||||
|
|
||||||
public WorkingArea(ILogger logger, INativeMethods nativeMethods)
|
|
||||||
{
|
|
||||||
this.logger = logger;
|
|
||||||
this.nativeMethods = nativeMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InitializeFor(ITaskbar taskbar)
|
|
||||||
{
|
|
||||||
originalWorkingArea = nativeMethods.GetWorkingArea();
|
|
||||||
|
|
||||||
LogWorkingArea("Saved original working area", originalWorkingArea);
|
|
||||||
|
|
||||||
var area = new Bounds
|
|
||||||
{
|
|
||||||
Left = 0,
|
|
||||||
Top = 0,
|
|
||||||
Right = Screen.PrimaryScreen.Bounds.Width,
|
|
||||||
Bottom = Screen.PrimaryScreen.Bounds.Height - taskbar.GetAbsoluteHeight()
|
|
||||||
};
|
|
||||||
|
|
||||||
LogWorkingArea("Trying to set new working area", area);
|
|
||||||
nativeMethods.SetWorkingArea(area);
|
|
||||||
LogWorkingArea("Working area is now set to", nativeMethods.GetWorkingArea());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
if (originalWorkingArea != null)
|
|
||||||
{
|
|
||||||
nativeMethods.SetWorkingArea(originalWorkingArea);
|
|
||||||
LogWorkingArea("Restored original working area", originalWorkingArea);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LogWorkingArea(string message, IBounds area)
|
|
||||||
{
|
|
||||||
logger.Info($"{message}: Left = {area.Left}, Top = {area.Top}, Right = {area.Right}, Bottom = {area.Bottom}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +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 SafeExamBrowser.Contracts.UserInterface;
|
|
||||||
|
|
||||||
namespace SafeExamBrowser.Contracts.Configuration
|
|
||||||
{
|
|
||||||
public interface IWorkingArea
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the Windows working area to accommodate to the taskbar's dimensions.
|
|
||||||
/// </summary>
|
|
||||||
void InitializeFor(ITaskbar taskbar);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resets the Windows working area to its previous (initial) state.
|
|
||||||
/// </summary>
|
|
||||||
void Reset();
|
|
||||||
}
|
|
||||||
}
|
|
41
SafeExamBrowser.Contracts/Monitoring/IDisplayMonitor.cs
Normal file
41
SafeExamBrowser.Contracts/Monitoring/IDisplayMonitor.cs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.Contracts.Monitoring
|
||||||
|
{
|
||||||
|
public delegate void DisplayChangedEventHandler();
|
||||||
|
|
||||||
|
public interface IDisplayMonitor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event fired when the primary display or its settings have changed.
|
||||||
|
/// </summary>
|
||||||
|
event DisplayChangedEventHandler DisplayChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the desktop working area to accommodate to the taskbar's height, removes the configured wallpaper (if possible) and
|
||||||
|
/// prevents the computer from entering sleep mode or turning its display(s) off.
|
||||||
|
/// </summary>
|
||||||
|
void InitializePrimaryDisplay(int taskbarHeight);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the desktop working area and wallpaper to their previous (initial) state.
|
||||||
|
/// </summary>
|
||||||
|
void ResetPrimaryDisplay();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts monitoring for display changes, i.e. display changes will trigger the <c>DisplaySettingsChanged</c> event.
|
||||||
|
/// </summary>
|
||||||
|
void StartMonitoringDisplayChanges();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops monitoring for display changes.
|
||||||
|
/// </summary>
|
||||||
|
void StopMonitoringDisplayChanges();
|
||||||
|
}
|
||||||
|
}
|
|
@ -72,7 +72,6 @@
|
||||||
<Compile Include="Configuration\Settings\ISettings.cs" />
|
<Compile Include="Configuration\Settings\ISettings.cs" />
|
||||||
<Compile Include="Behaviour\IShutdownController.cs" />
|
<Compile Include="Behaviour\IShutdownController.cs" />
|
||||||
<Compile Include="Behaviour\IStartupController.cs" />
|
<Compile Include="Behaviour\IStartupController.cs" />
|
||||||
<Compile Include="Configuration\IWorkingArea.cs" />
|
|
||||||
<Compile Include="I18n\IText.cs" />
|
<Compile Include="I18n\IText.cs" />
|
||||||
<Compile Include="I18n\TextKey.cs" />
|
<Compile Include="I18n\TextKey.cs" />
|
||||||
<Compile Include="Logging\ILogContent.cs" />
|
<Compile Include="Logging\ILogContent.cs" />
|
||||||
|
@ -83,6 +82,7 @@
|
||||||
<Compile Include="Logging\ILogText.cs" />
|
<Compile Include="Logging\ILogText.cs" />
|
||||||
<Compile Include="Logging\IThreadInfo.cs" />
|
<Compile Include="Logging\IThreadInfo.cs" />
|
||||||
<Compile Include="Logging\LogLevel.cs" />
|
<Compile Include="Logging\LogLevel.cs" />
|
||||||
|
<Compile Include="Monitoring\IDisplayMonitor.cs" />
|
||||||
<Compile Include="Monitoring\IKeyboardInterceptor.cs" />
|
<Compile Include="Monitoring\IKeyboardInterceptor.cs" />
|
||||||
<Compile Include="Monitoring\IMouseInterceptor.cs" />
|
<Compile Include="Monitoring\IMouseInterceptor.cs" />
|
||||||
<Compile Include="Monitoring\IProcessMonitor.cs" />
|
<Compile Include="Monitoring\IProcessMonitor.cs" />
|
||||||
|
|
|
@ -38,6 +38,11 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
||||||
/// </exception>
|
/// </exception>
|
||||||
void DeregisterSystemEvent(IntPtr handle);
|
void DeregisterSystemEvent(IntPtr handle);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prevents Windows from entering sleep mode and keeps all displays powered on.
|
||||||
|
/// </summary>
|
||||||
|
void DisableSleep();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empties the clipboard.
|
/// Empties the clipboard.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -72,8 +77,15 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
||||||
uint GetShellProcessId();
|
uint GetShellProcessId();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the title of the specified window, or an empty string, if the
|
/// Retrieves the path of the currently configured wallpaper image, or an empty string, if there is no wallpaper set.
|
||||||
/// given window does not have a title.
|
/// </summary>
|
||||||
|
/// <exception cref="System.ComponentModel.Win32Exception">
|
||||||
|
/// If the wallpaper path could not be retrieved.
|
||||||
|
/// </exception>
|
||||||
|
string GetWallpaperPath();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the title of the specified window, or an empty string, if the given window does not have a title.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string GetWindowTitle(IntPtr window);
|
string GetWindowTitle(IntPtr window);
|
||||||
|
|
||||||
|
@ -125,6 +137,14 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IntPtr RegisterSystemCaptureStartEvent(Action<IntPtr> callback);
|
IntPtr RegisterSystemCaptureStartEvent(Action<IntPtr> callback);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the currently configured desktop wallpaper.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="System.ComponentModel.Win32Exception">
|
||||||
|
/// If the wallpaper could not be removed.
|
||||||
|
/// </exception>
|
||||||
|
void RemoveWallpaper();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restores the specified window to its original size and position.
|
/// Restores the specified window to its original size and position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -135,6 +155,14 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void SendCloseMessageTo(IntPtr window);
|
void SendCloseMessageTo(IntPtr window);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the wallpaper to the image located at the specified file path.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="System.ComponentModel.Win32Exception">
|
||||||
|
/// If the wallpaper could not be set.
|
||||||
|
/// </exception>
|
||||||
|
void SetWallpaper(string filePath);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the working area of the primary screen according to the given dimensions.
|
/// Sets the working area of the primary screen according to the given dimensions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -10,7 +10,6 @@ using System;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
using SafeExamBrowser.Contracts.Behaviour;
|
using SafeExamBrowser.Contracts.Behaviour;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Contracts.Monitoring;
|
using SafeExamBrowser.Contracts.Monitoring;
|
||||||
using SafeExamBrowser.Contracts.UserInterface;
|
using SafeExamBrowser.Contracts.UserInterface;
|
||||||
|
@ -21,33 +20,52 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class RuntimeControllerTests
|
public class RuntimeControllerTests
|
||||||
{
|
{
|
||||||
|
private Mock<IDisplayMonitor> displayMonitorMock;
|
||||||
private Mock<ILogger> loggerMock;
|
private Mock<ILogger> loggerMock;
|
||||||
private Mock<IProcessMonitor> processMonitorMock;
|
private Mock<IProcessMonitor> processMonitorMock;
|
||||||
private Mock<ITaskbar> taskbarMock;
|
private Mock<ITaskbar> taskbarMock;
|
||||||
private Mock<IWindowMonitor> windowMonitorMock;
|
private Mock<IWindowMonitor> windowMonitorMock;
|
||||||
private Mock<IWorkingArea> workingAreaMock;
|
|
||||||
|
|
||||||
private IRuntimeController sut;
|
private IRuntimeController sut;
|
||||||
|
|
||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
|
displayMonitorMock = new Mock<IDisplayMonitor>();
|
||||||
loggerMock = new Mock<ILogger>();
|
loggerMock = new Mock<ILogger>();
|
||||||
processMonitorMock = new Mock<IProcessMonitor>();
|
processMonitorMock = new Mock<IProcessMonitor>();
|
||||||
taskbarMock = new Mock<ITaskbar>();
|
taskbarMock = new Mock<ITaskbar>();
|
||||||
windowMonitorMock= new Mock<IWindowMonitor>();
|
windowMonitorMock= new Mock<IWindowMonitor>();
|
||||||
workingAreaMock = new Mock<IWorkingArea>();
|
|
||||||
|
|
||||||
sut = new RuntimeController(
|
sut = new RuntimeController(
|
||||||
|
displayMonitorMock.Object,
|
||||||
loggerMock.Object,
|
loggerMock.Object,
|
||||||
processMonitorMock.Object,
|
processMonitorMock.Object,
|
||||||
taskbarMock.Object,
|
taskbarMock.Object,
|
||||||
windowMonitorMock.Object,
|
windowMonitorMock.Object);
|
||||||
workingAreaMock.Object);
|
|
||||||
|
|
||||||
sut.Start();
|
sut.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void MustHandleDisplayChangeCorrectly()
|
||||||
|
{
|
||||||
|
var order = 0;
|
||||||
|
var workingArea = 0;
|
||||||
|
var taskbar = 0;
|
||||||
|
|
||||||
|
displayMonitorMock.Setup(w => w.InitializePrimaryDisplay(taskbarMock.Object.GetAbsoluteHeight())).Callback(() => workingArea = ++order);
|
||||||
|
taskbarMock.Setup(t => t.InitializeBounds()).Callback(() => taskbar = ++order);
|
||||||
|
|
||||||
|
displayMonitorMock.Raise(d => d.DisplayChanged += null);
|
||||||
|
|
||||||
|
displayMonitorMock.Verify(w => w.InitializePrimaryDisplay(taskbarMock.Object.GetAbsoluteHeight()), Times.Once);
|
||||||
|
taskbarMock.Verify(t => t.InitializeBounds(), Times.Once);
|
||||||
|
|
||||||
|
Assert.IsTrue(workingArea == 1);
|
||||||
|
Assert.IsTrue(taskbar == 2);
|
||||||
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustHandleExplorerStartCorrectly()
|
public void MustHandleExplorerStartCorrectly()
|
||||||
{
|
{
|
||||||
|
@ -57,13 +75,13 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour
|
||||||
var taskbar = 0;
|
var taskbar = 0;
|
||||||
|
|
||||||
processMonitorMock.Setup(p => p.CloseExplorerShell()).Callback(() => processManager = ++order);
|
processMonitorMock.Setup(p => p.CloseExplorerShell()).Callback(() => processManager = ++order);
|
||||||
workingAreaMock.Setup(w => w.InitializeFor(taskbarMock.Object)).Callback(() => workingArea = ++order);
|
displayMonitorMock.Setup(w => w.InitializePrimaryDisplay(taskbarMock.Object.GetAbsoluteHeight())).Callback(() => workingArea = ++order);
|
||||||
taskbarMock.Setup(t => t.InitializeBounds()).Callback(() => taskbar = ++order);
|
taskbarMock.Setup(t => t.InitializeBounds()).Callback(() => taskbar = ++order);
|
||||||
|
|
||||||
processMonitorMock.Raise(p => p.ExplorerStarted += null);
|
processMonitorMock.Raise(p => p.ExplorerStarted += null);
|
||||||
|
|
||||||
processMonitorMock.Verify(p => p.CloseExplorerShell(), Times.Once);
|
processMonitorMock.Verify(p => p.CloseExplorerShell(), Times.Once);
|
||||||
workingAreaMock.Verify(w => w.InitializeFor(taskbarMock.Object), Times.Once);
|
displayMonitorMock.Verify(w => w.InitializePrimaryDisplay(taskbarMock.Object.GetAbsoluteHeight()), Times.Once);
|
||||||
taskbarMock.Verify(t => t.InitializeBounds(), Times.Once);
|
taskbarMock.Verify(t => t.InitializeBounds(), Times.Once);
|
||||||
|
|
||||||
Assert.IsTrue(processManager == 1);
|
Assert.IsTrue(processManager == 1);
|
||||||
|
|
|
@ -7,26 +7,26 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using SafeExamBrowser.Contracts.Behaviour;
|
using SafeExamBrowser.Contracts.Behaviour;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
|
||||||
using SafeExamBrowser.Contracts.I18n;
|
using SafeExamBrowser.Contracts.I18n;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
using SafeExamBrowser.Contracts.Monitoring;
|
||||||
using SafeExamBrowser.Contracts.UserInterface;
|
using SafeExamBrowser.Contracts.UserInterface;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Core.Behaviour.Operations
|
namespace SafeExamBrowser.Core.Behaviour.Operations
|
||||||
{
|
{
|
||||||
public class WorkingAreaOperation : IOperation
|
public class DisplayMonitorOperation : IOperation
|
||||||
{
|
{
|
||||||
|
private IDisplayMonitor displayMonitor;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private ITaskbar taskbar;
|
private ITaskbar taskbar;
|
||||||
private IWorkingArea workingArea;
|
|
||||||
|
|
||||||
public ISplashScreen SplashScreen { private get; set; }
|
public ISplashScreen SplashScreen { private get; set; }
|
||||||
|
|
||||||
public WorkingAreaOperation(ILogger logger, ITaskbar taskbar, IWorkingArea workingArea)
|
public DisplayMonitorOperation(IDisplayMonitor displayMonitor, ILogger logger, ITaskbar taskbar)
|
||||||
{
|
{
|
||||||
|
this.displayMonitor = displayMonitor;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.taskbar = taskbar;
|
this.taskbar = taskbar;
|
||||||
this.workingArea = workingArea;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Perform()
|
public void Perform()
|
||||||
|
@ -34,10 +34,8 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
||||||
logger.Info("Initializing working area...");
|
logger.Info("Initializing working area...");
|
||||||
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeWorkingArea);
|
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeWorkingArea);
|
||||||
|
|
||||||
// TODO
|
displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight());
|
||||||
// - Emptying clipboard
|
displayMonitor.StartMonitoringDisplayChanges();
|
||||||
|
|
||||||
workingArea.InitializeFor(taskbar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Revert()
|
public void Revert()
|
||||||
|
@ -45,10 +43,8 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
||||||
logger.Info("Restoring working area...");
|
logger.Info("Restoring working area...");
|
||||||
SplashScreen.UpdateText(TextKey.SplashScreen_RestoreWorkingArea);
|
SplashScreen.UpdateText(TextKey.SplashScreen_RestoreWorkingArea);
|
||||||
|
|
||||||
// TODO
|
displayMonitor.StopMonitoringDisplayChanges();
|
||||||
// - Emptying clipboard
|
displayMonitor.ResetPrimaryDisplay();
|
||||||
|
|
||||||
workingArea.Reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using SafeExamBrowser.Contracts.Behaviour;
|
using SafeExamBrowser.Contracts.Behaviour;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Contracts.Monitoring;
|
using SafeExamBrowser.Contracts.Monitoring;
|
||||||
using SafeExamBrowser.Contracts.UserInterface;
|
using SafeExamBrowser.Contracts.UserInterface;
|
||||||
|
@ -17,28 +16,29 @@ namespace SafeExamBrowser.Core.Behaviour
|
||||||
{
|
{
|
||||||
public class RuntimeController : IRuntimeController
|
public class RuntimeController : IRuntimeController
|
||||||
{
|
{
|
||||||
|
private IDisplayMonitor displayMonitor;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private IProcessMonitor processMonitor;
|
private IProcessMonitor processMonitor;
|
||||||
private ITaskbar taskbar;
|
private ITaskbar taskbar;
|
||||||
private IWindowMonitor windowMonitor;
|
private IWindowMonitor windowMonitor;
|
||||||
private IWorkingArea workingArea;
|
|
||||||
|
|
||||||
public RuntimeController(
|
public RuntimeController(
|
||||||
|
IDisplayMonitor displayMonitor,
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
IProcessMonitor processMonitor,
|
IProcessMonitor processMonitor,
|
||||||
ITaskbar taskbar,
|
ITaskbar taskbar,
|
||||||
IWindowMonitor windowMonitor,
|
IWindowMonitor windowMonitor)
|
||||||
IWorkingArea workingArea)
|
|
||||||
{
|
{
|
||||||
|
this.displayMonitor = displayMonitor;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.processMonitor = processMonitor;
|
this.processMonitor = processMonitor;
|
||||||
this.taskbar = taskbar;
|
this.taskbar = taskbar;
|
||||||
this.windowMonitor = windowMonitor;
|
this.windowMonitor = windowMonitor;
|
||||||
this.workingArea = workingArea;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
|
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
|
||||||
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
|
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
|
||||||
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
|
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
|
||||||
}
|
}
|
||||||
|
@ -49,12 +49,21 @@ namespace SafeExamBrowser.Core.Behaviour
|
||||||
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
|
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DisplayMonitor_DisplaySettingsChanged()
|
||||||
|
{
|
||||||
|
logger.Info("Reinitializing working area...");
|
||||||
|
displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight());
|
||||||
|
logger.Info("Reinitializing taskbar bounds...");
|
||||||
|
taskbar.InitializeBounds();
|
||||||
|
logger.Info("Desktop successfully restored.");
|
||||||
|
}
|
||||||
|
|
||||||
private void ProcessMonitor_ExplorerStarted()
|
private void ProcessMonitor_ExplorerStarted()
|
||||||
{
|
{
|
||||||
logger.Info("Trying to shut down explorer...");
|
logger.Info("Trying to shut down explorer...");
|
||||||
processMonitor.CloseExplorerShell();
|
processMonitor.CloseExplorerShell();
|
||||||
logger.Info("Reinitializing working area...");
|
logger.Info("Reinitializing working area...");
|
||||||
workingArea.InitializeFor(taskbar);
|
displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight());
|
||||||
logger.Info("Reinitializing taskbar bounds...");
|
logger.Info("Reinitializing taskbar bounds...");
|
||||||
taskbar.InitializeBounds();
|
taskbar.InitializeBounds();
|
||||||
logger.Info("Desktop successfully restored.");
|
logger.Info("Desktop successfully restored.");
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
<Compile Include="Behaviour\Operations\ProcessMonitorOperation.cs" />
|
<Compile Include="Behaviour\Operations\ProcessMonitorOperation.cs" />
|
||||||
<Compile Include="Behaviour\Operations\TaskbarOperation.cs" />
|
<Compile Include="Behaviour\Operations\TaskbarOperation.cs" />
|
||||||
<Compile Include="Behaviour\Operations\WindowMonitorOperation.cs" />
|
<Compile Include="Behaviour\Operations\WindowMonitorOperation.cs" />
|
||||||
<Compile Include="Behaviour\Operations\WorkingAreaOperation.cs" />
|
<Compile Include="Behaviour\Operations\DisplayMonitorOperation.cs" />
|
||||||
<Compile Include="Behaviour\ShutdownController.cs" />
|
<Compile Include="Behaviour\ShutdownController.cs" />
|
||||||
<Compile Include="Behaviour\StartupController.cs" />
|
<Compile Include="Behaviour\StartupController.cs" />
|
||||||
<Compile Include="Logging\DefaultLogFormatter.cs" />
|
<Compile Include="Logging\DefaultLogFormatter.cs" />
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
using SafeExamBrowser.Contracts.WindowsApi;
|
using SafeExamBrowser.Contracts.WindowsApi;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration
|
namespace SafeExamBrowser.Monitoring.Display
|
||||||
{
|
{
|
||||||
internal class Bounds : IBounds
|
internal class Bounds : IBounds
|
||||||
{
|
{
|
139
SafeExamBrowser.Monitoring/Display/DisplayMonitor.cs
Normal file
139
SafeExamBrowser.Monitoring/Display/DisplayMonitor.cs
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
using SafeExamBrowser.Contracts.Monitoring;
|
||||||
|
using SafeExamBrowser.Contracts.WindowsApi;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Monitoring.Display
|
||||||
|
{
|
||||||
|
public class DisplayMonitor : IDisplayMonitor
|
||||||
|
{
|
||||||
|
private IBounds originalWorkingArea;
|
||||||
|
private ILogger logger;
|
||||||
|
private INativeMethods nativeMethods;
|
||||||
|
private string wallpaper;
|
||||||
|
|
||||||
|
public event DisplayChangedEventHandler DisplayChanged;
|
||||||
|
|
||||||
|
public DisplayMonitor(ILogger logger, INativeMethods nativeMethods)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
this.nativeMethods = nativeMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializePrimaryDisplay(int taskbarHeight)
|
||||||
|
{
|
||||||
|
InitializeWorkingArea(taskbarHeight);
|
||||||
|
InitializeWallpaper();
|
||||||
|
PreventSleepMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetPrimaryDisplay()
|
||||||
|
{
|
||||||
|
ResetWorkingArea();
|
||||||
|
ResetWallpaper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartMonitoringDisplayChanges()
|
||||||
|
{
|
||||||
|
SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
|
||||||
|
logger.Info("Started monitoring display changes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopMonitoringDisplayChanges()
|
||||||
|
{
|
||||||
|
SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged;
|
||||||
|
logger.Info("Stopped monitoring display changes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
logger.Info("Display change detected!");
|
||||||
|
DisplayChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeWorkingArea(int taskbarHeight)
|
||||||
|
{
|
||||||
|
var identifier = GetIdentifierForPrimaryDisplay();
|
||||||
|
|
||||||
|
originalWorkingArea = nativeMethods.GetWorkingArea();
|
||||||
|
LogWorkingArea($"Saved original working area for {identifier}", originalWorkingArea);
|
||||||
|
|
||||||
|
var area = new Bounds
|
||||||
|
{
|
||||||
|
Left = 0,
|
||||||
|
Top = 0,
|
||||||
|
Right = Screen.PrimaryScreen.Bounds.Width,
|
||||||
|
Bottom = Screen.PrimaryScreen.Bounds.Height - taskbarHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
LogWorkingArea($"Trying to set new working area for {identifier}", area);
|
||||||
|
nativeMethods.SetWorkingArea(area);
|
||||||
|
LogWorkingArea($"Working area of {identifier} is now set to", nativeMethods.GetWorkingArea());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeWallpaper()
|
||||||
|
{
|
||||||
|
var path = nativeMethods.GetWallpaperPath();
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
wallpaper = path;
|
||||||
|
logger.Info($"Saved wallpaper image: {wallpaper}.");
|
||||||
|
nativeMethods.RemoveWallpaper();
|
||||||
|
logger.Info("Removed current wallpaper.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PreventSleepMode()
|
||||||
|
{
|
||||||
|
nativeMethods.DisableSleep();
|
||||||
|
logger.Info("Disabled sleep mode and display timeout.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetWorkingArea()
|
||||||
|
{
|
||||||
|
var identifier = GetIdentifierForPrimaryDisplay();
|
||||||
|
|
||||||
|
if (originalWorkingArea != null)
|
||||||
|
{
|
||||||
|
nativeMethods.SetWorkingArea(originalWorkingArea);
|
||||||
|
LogWorkingArea($"Restored original working area for {identifier}", originalWorkingArea);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn($"Could not restore original working area for {identifier}!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetWallpaper()
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(wallpaper))
|
||||||
|
{
|
||||||
|
nativeMethods.SetWallpaper(wallpaper);
|
||||||
|
logger.Info($"Restored wallpaper image: {wallpaper}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetIdentifierForPrimaryDisplay()
|
||||||
|
{
|
||||||
|
var display = Screen.PrimaryScreen.DeviceName?.Replace(@"\\.\", string.Empty);
|
||||||
|
|
||||||
|
return $"{display} ({Screen.PrimaryScreen.Bounds.Width}x{Screen.PrimaryScreen.Bounds.Height})";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogWorkingArea(string message, IBounds area)
|
||||||
|
{
|
||||||
|
logger.Info($"{message}: Left = {area.Left}, Top = {area.Top}, Right = {area.Right}, Bottom = {area.Bottom}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,7 +50,9 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Management" />
|
<Reference Include="System.Management" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
@ -60,10 +62,13 @@
|
||||||
<Reference Include="WindowsBase" />
|
<Reference Include="WindowsBase" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Display\Bounds.cs" />
|
||||||
|
<Compile Include="Display\DisplayMonitor.cs" />
|
||||||
<Compile Include="Keyboard\KeyboardInterceptor.cs" />
|
<Compile Include="Keyboard\KeyboardInterceptor.cs" />
|
||||||
<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>
|
||||||
|
|
18
SafeExamBrowser.Monitoring/Windows/Window.cs
Normal file
18
SafeExamBrowser.Monitoring/Windows/Window.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Monitoring.Windows
|
||||||
|
{
|
||||||
|
internal struct Window
|
||||||
|
{
|
||||||
|
internal IntPtr Handle { get; set; }
|
||||||
|
internal string Title { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -117,11 +117,5 @@ namespace SafeExamBrowser.Monitoring.Windows
|
||||||
{
|
{
|
||||||
WindowChanged?.Invoke(window);
|
WindowChanged?.Invoke(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct Window
|
|
||||||
{
|
|
||||||
internal IntPtr Handle { get; set; }
|
|
||||||
internal string Title { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,19 @@ using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
using SafeExamBrowser.Contracts.UserInterface;
|
using SafeExamBrowser.Contracts.UserInterface;
|
||||||
|
|
||||||
namespace SafeExamBrowser.UserInterface
|
namespace SafeExamBrowser.UserInterface
|
||||||
{
|
{
|
||||||
public partial class Taskbar : Window, ITaskbar
|
public partial class Taskbar : Window, ITaskbar
|
||||||
{
|
{
|
||||||
public Taskbar()
|
private ILogger logger;
|
||||||
|
|
||||||
|
public Taskbar(ILogger logger)
|
||||||
{
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Loaded += (o, args) => InitializeBounds();
|
Loaded += (o, args) => InitializeBounds();
|
||||||
|
@ -41,28 +46,13 @@ namespace SafeExamBrowser.UserInterface
|
||||||
|
|
||||||
public int GetAbsoluteHeight()
|
public int GetAbsoluteHeight()
|
||||||
{
|
{
|
||||||
// WPF works with device-independent pixels. The following code is required
|
|
||||||
// to get the real height of the taskbar (in absolute, device-specific pixels).
|
|
||||||
// Source: https://stackoverflow.com/questions/3286175/how-do-i-convert-a-wpf-size-to-physical-pixels
|
|
||||||
|
|
||||||
return Dispatcher.Invoke(() =>
|
return Dispatcher.Invoke(() =>
|
||||||
{
|
{
|
||||||
Matrix transformToDevice;
|
var height = (int) TransformToPhysical(Width, Height).Y;
|
||||||
var source = PresentationSource.FromVisual(this);
|
|
||||||
|
|
||||||
if (source != null)
|
logger.Info($"Calculated absolute taskbar height is {height}px.");
|
||||||
{
|
|
||||||
transformToDevice = source.CompositionTarget.TransformToDevice;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using (var newSource = new HwndSource(new HwndSourceParameters()))
|
|
||||||
{
|
|
||||||
transformToDevice = newSource.CompositionTarget.TransformToDevice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)transformToDevice.Transform((Vector)new Size(Width, Height)).Y;
|
return height;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +63,11 @@ namespace SafeExamBrowser.UserInterface
|
||||||
Width = SystemParameters.WorkArea.Right;
|
Width = SystemParameters.WorkArea.Right;
|
||||||
Left = SystemParameters.WorkArea.Right - Width;
|
Left = SystemParameters.WorkArea.Right - Width;
|
||||||
Top = SystemParameters.WorkArea.Bottom;
|
Top = SystemParameters.WorkArea.Bottom;
|
||||||
|
|
||||||
|
var position = TransformToPhysical(Left, Top);
|
||||||
|
var size = TransformToPhysical(Width, Height);
|
||||||
|
|
||||||
|
logger.Info($"Set taskbar bounds to {Width}x{Height} at ({Left}/{Top}), in physical pixels: {size.X}x{size.Y} at ({position.X}/{position.Y}).");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,5 +101,29 @@ namespace SafeExamBrowser.UserInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Vector TransformToPhysical(double x, double y)
|
||||||
|
{
|
||||||
|
// WPF works with device-independent pixels. The following code is required
|
||||||
|
// to transform those values to their absolute, device-specific pixel value.
|
||||||
|
// Source: https://stackoverflow.com/questions/3286175/how-do-i-convert-a-wpf-size-to-physical-pixels
|
||||||
|
|
||||||
|
Matrix transformToDevice;
|
||||||
|
var source = PresentationSource.FromVisual(this);
|
||||||
|
|
||||||
|
if (source != null)
|
||||||
|
{
|
||||||
|
transformToDevice = source.CompositionTarget.TransformToDevice;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using (var newSource = new HwndSource(new HwndSourceParameters()))
|
||||||
|
{
|
||||||
|
transformToDevice = newSource.CompositionTarget.TransformToDevice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformToDevice.Transform(new Vector(x, y));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,11 @@ namespace SafeExamBrowser.WindowsApi.Constants
|
||||||
internal enum SPI : uint
|
internal enum SPI : uint
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the size of the work area. The work area is the portion of the screen not obscured by the system taskbar
|
/// Retrieves the full path of the bitmap file for the desktop wallpaper. The pvParam parameter must point to a buffer
|
||||||
/// or by application desktop toolbars. The pvParam parameter is a pointer to a RECT structure that specifies the
|
/// that receives a null-terminated path string. Set the uiParam parameter to the size, in characters, of the pvParam buffer.
|
||||||
/// new work area rectangle, expressed in virtual screen coordinates. In a system with multiple display monitors,
|
/// The returned string will not exceed MAX_PATH characters. If there is no desktop wallpaper, the returned string is empty.
|
||||||
/// the function sets the work area of the monitor that contains the specified rectangle.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SETWORKAREA = 0x002F,
|
GETDESKWALLPAPER = 0x73,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the size of the work area on the primary display monitor. The work area is the portion of the screen
|
/// Retrieves the size of the work area on the primary display monitor. The work area is the portion of the screen
|
||||||
|
@ -27,6 +26,21 @@ namespace SafeExamBrowser.WindowsApi.Constants
|
||||||
/// RECT structure that receives the coordinates of the work area, expressed in virtual screen coordinates. To get
|
/// RECT structure that receives the coordinates of the work area, expressed in virtual screen coordinates. To get
|
||||||
/// the work area of a monitor other than the primary display monitor, call the GetMonitorInfo function.
|
/// the work area of a monitor other than the primary display monitor, call the GetMonitorInfo function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
GETWORKAREA = 0x0030,
|
GETWORKAREA = 0x30,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the desktop wallpaper. The value of the pvParam parameter determines the new wallpaper. To specify a wallpaper bitmap,
|
||||||
|
/// set pvParam to point to a null-terminated string containing the name of a bitmap file. Setting pvParam to "" removes the
|
||||||
|
/// wallpaper. Setting pvParam to SETWALLPAPER_DEFAULT or null reverts to the default wallpaper.
|
||||||
|
/// </summary>
|
||||||
|
SETDESKWALLPAPER = 0x14,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the size of the work area. The work area is the portion of the screen not obscured by the system taskbar
|
||||||
|
/// or by application desktop toolbars. The pvParam parameter is a pointer to a RECT structure that specifies the
|
||||||
|
/// new work area rectangle, expressed in virtual screen coordinates. In a system with multiple display monitors,
|
||||||
|
/// the function sets the work area of the monitor that contains the specified rectangle.
|
||||||
|
/// </summary>
|
||||||
|
SETWORKAREA = 0x2F,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using SafeExamBrowser.WindowsApi.Types;
|
||||||
|
|
||||||
namespace SafeExamBrowser.WindowsApi
|
namespace SafeExamBrowser.WindowsApi
|
||||||
{
|
{
|
||||||
|
@ -16,7 +17,10 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class Kernel32
|
internal class Kernel32
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
internal static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
internal static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,11 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
EventDelegates.TryRemove(handle, out EventProc d);
|
EventDelegates.TryRemove(handle, out EventProc d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DisableSleep()
|
||||||
|
{
|
||||||
|
Kernel32.SetThreadExecutionState(EXECUTION_STATE.CONTINUOUS | EXECUTION_STATE.DISPLAY_REQUIRED | EXECUTION_STATE.SYSTEM_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
public void EmptyClipboard()
|
public void EmptyClipboard()
|
||||||
{
|
{
|
||||||
var success = true;
|
var success = true;
|
||||||
|
@ -149,6 +154,22 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
return processId;
|
return processId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetWallpaperPath()
|
||||||
|
{
|
||||||
|
const int MAX_PATH = 260;
|
||||||
|
var buffer = new String('\0', MAX_PATH);
|
||||||
|
var success = User32.SystemParametersInfo(SPI.GETDESKWALLPAPER, buffer.Length, buffer, 0);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = buffer.Substring(0, buffer.IndexOf('\0'));
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
public string GetWindowTitle(IntPtr window)
|
public string GetWindowTitle(IntPtr window)
|
||||||
{
|
{
|
||||||
var length = User32.GetWindowTextLength(window);
|
var length = User32.GetWindowTextLength(window);
|
||||||
|
@ -256,6 +277,11 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveWallpaper()
|
||||||
|
{
|
||||||
|
SetWallpaper(string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
public void RestoreWindow(IntPtr window)
|
public void RestoreWindow(IntPtr window)
|
||||||
{
|
{
|
||||||
User32.ShowWindow(window, (int)ShowWindowCommand.Restore);
|
User32.ShowWindow(window, (int)ShowWindowCommand.Restore);
|
||||||
|
@ -266,6 +292,16 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
User32.SendMessage(window, Constant.WM_SYSCOMMAND, (IntPtr) SystemCommand.CLOSE, IntPtr.Zero);
|
User32.SendMessage(window, Constant.WM_SYSCOMMAND, (IntPtr) SystemCommand.CLOSE, IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetWallpaper(string filePath)
|
||||||
|
{
|
||||||
|
var success = User32.SystemParametersInfo(SPI.SETDESKWALLPAPER, 0, filePath, SPIF.UPDATEANDCHANGE);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SetWorkingArea(IBounds bounds)
|
public void SetWorkingArea(IBounds bounds)
|
||||||
{
|
{
|
||||||
var workingArea = new RECT { Left = bounds.Left, Top = bounds.Top, Right = bounds.Right, Bottom = bounds.Bottom };
|
var workingArea = new RECT { Left = bounds.Left, Top = bounds.Top, Right = bounds.Right, Bottom = bounds.Bottom };
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
<Compile Include="Constants\HookType.cs" />
|
<Compile Include="Constants\HookType.cs" />
|
||||||
<Compile Include="Monitoring\MouseHook.cs" />
|
<Compile Include="Monitoring\MouseHook.cs" />
|
||||||
<Compile Include="Types\Bounds.cs" />
|
<Compile Include="Types\Bounds.cs" />
|
||||||
|
<Compile Include="Types\EXECUTION_STATE.cs" />
|
||||||
<Compile Include="Types\KBDLLHOOKSTRUCT.cs" />
|
<Compile Include="Types\KBDLLHOOKSTRUCT.cs" />
|
||||||
<Compile Include="Types\KBDLLHOOKSTRUCTFlags.cs" />
|
<Compile Include="Types\KBDLLHOOKSTRUCTFlags.cs" />
|
||||||
<Compile Include="Constants\ShowWindowCommand.cs" />
|
<Compile Include="Constants\ShowWindowCommand.cs" />
|
||||||
|
|
25
SafeExamBrowser.WindowsApi/Types/EXECUTION_STATE.cs
Normal file
25
SafeExamBrowser.WindowsApi/Types/EXECUTION_STATE.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.WindowsApi.Types
|
||||||
|
{
|
||||||
|
/// <remarks>
|
||||||
|
/// See http://www.pinvoke.net/default.aspx/kernel32/SetThreadExecutionState.html.
|
||||||
|
/// See https://msdn.microsoft.com/en-us/library/aa373208(v=vs.85).aspx.
|
||||||
|
/// </remarks>
|
||||||
|
[Flags]
|
||||||
|
public enum EXECUTION_STATE : uint
|
||||||
|
{
|
||||||
|
AWAYMODE_REQUIRED = 0x00000040,
|
||||||
|
CONTINUOUS = 0x80000000,
|
||||||
|
DISPLAY_REQUIRED = 0x00000002,
|
||||||
|
SYSTEM_REQUIRED = 0x00000001
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,9 +11,9 @@ using SafeExamBrowser.Contracts.WindowsApi;
|
||||||
|
|
||||||
namespace SafeExamBrowser.WindowsApi.Types
|
namespace SafeExamBrowser.WindowsApi.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <remarks>
|
||||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd162897(v=vs.85).aspx.
|
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd162897(v=vs.85).aspx.
|
||||||
/// </summary>
|
/// </remarks>
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
internal struct RECT
|
internal struct RECT
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,10 +38,10 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
internal static extern bool EnumWindows(EnumWindowsDelegate enumProc, IntPtr lParam);
|
internal static extern bool EnumWindows(EnumWindowsDelegate enumProc, IntPtr lParam);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
|
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
@ -79,6 +79,10 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
internal static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref RECT pvParam, SPIF fWinIni);
|
internal static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref RECT pvParam, SPIF fWinIni);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
internal static extern bool SystemParametersInfo(SPI uiAction, int uiParam, string pvParam, SPIF fWinIni);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
|
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ using SafeExamBrowser.Core.Behaviour;
|
||||||
using SafeExamBrowser.Core.Behaviour.Operations;
|
using SafeExamBrowser.Core.Behaviour.Operations;
|
||||||
using SafeExamBrowser.Core.I18n;
|
using SafeExamBrowser.Core.I18n;
|
||||||
using SafeExamBrowser.Core.Logging;
|
using SafeExamBrowser.Core.Logging;
|
||||||
|
using SafeExamBrowser.Monitoring.Display;
|
||||||
using SafeExamBrowser.Monitoring.Keyboard;
|
using SafeExamBrowser.Monitoring.Keyboard;
|
||||||
using SafeExamBrowser.Monitoring.Mouse;
|
using SafeExamBrowser.Monitoring.Mouse;
|
||||||
using SafeExamBrowser.Monitoring.Processes;
|
using SafeExamBrowser.Monitoring.Processes;
|
||||||
|
@ -34,6 +35,7 @@ namespace SafeExamBrowser
|
||||||
{
|
{
|
||||||
private IApplicationController browserController;
|
private IApplicationController browserController;
|
||||||
private IApplicationInfo browserInfo;
|
private IApplicationInfo browserInfo;
|
||||||
|
private IDisplayMonitor displayMonitor;
|
||||||
private IKeyboardInterceptor keyboardInterceptor;
|
private IKeyboardInterceptor keyboardInterceptor;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private ILogContentFormatter logFormatter;
|
private ILogContentFormatter logFormatter;
|
||||||
|
@ -46,7 +48,6 @@ namespace SafeExamBrowser
|
||||||
private ITextResource textResource;
|
private ITextResource textResource;
|
||||||
private IUserInterfaceFactory uiFactory;
|
private IUserInterfaceFactory uiFactory;
|
||||||
private IWindowMonitor windowMonitor;
|
private IWindowMonitor windowMonitor;
|
||||||
private IWorkingArea workingArea;
|
|
||||||
|
|
||||||
public IShutdownController ShutdownController { get; private set; }
|
public IShutdownController ShutdownController { get; private set; }
|
||||||
public IStartupController StartupController { get; private set; }
|
public IStartupController StartupController { get; private set; }
|
||||||
|
@ -59,22 +60,22 @@ namespace SafeExamBrowser
|
||||||
logger = new Logger();
|
logger = new Logger();
|
||||||
logFormatter = new DefaultLogFormatter();
|
logFormatter = new DefaultLogFormatter();
|
||||||
nativeMethods = new NativeMethods();
|
nativeMethods = new NativeMethods();
|
||||||
settings = new SettingsImpl();
|
settings = new Settings();
|
||||||
Taskbar = new Taskbar();
|
|
||||||
textResource = new XmlTextResource();
|
textResource = new XmlTextResource();
|
||||||
uiFactory = new UserInterfaceFactory();
|
uiFactory = new UserInterfaceFactory();
|
||||||
|
|
||||||
logger.Subscribe(new LogFileWriter(logFormatter, settings));
|
logger.Subscribe(new LogFileWriter(logFormatter, settings));
|
||||||
|
|
||||||
text = new Text(textResource);
|
text = new Text(textResource);
|
||||||
|
Taskbar = new Taskbar(new ModuleLogger(logger, typeof(Taskbar)));
|
||||||
browserController = new BrowserApplicationController(settings, text, uiFactory);
|
browserController = new BrowserApplicationController(settings, text, uiFactory);
|
||||||
|
displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
|
||||||
keyboardInterceptor = new KeyboardInterceptor(settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor)));
|
keyboardInterceptor = new KeyboardInterceptor(settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor)));
|
||||||
mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), settings.Mouse);
|
mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), settings.Mouse);
|
||||||
processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
|
processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
|
||||||
windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods);
|
windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods);
|
||||||
workingArea = new WorkingArea(new ModuleLogger(logger, typeof(WorkingArea)), nativeMethods);
|
|
||||||
|
|
||||||
runtimeController = new RuntimeController(new ModuleLogger(logger, typeof(RuntimeController)), processMonitor, Taskbar, windowMonitor, workingArea);
|
runtimeController = new RuntimeController(displayMonitor, new ModuleLogger(logger, typeof(RuntimeController)), processMonitor, Taskbar, windowMonitor);
|
||||||
ShutdownController = new ShutdownController(logger, settings, text, uiFactory);
|
ShutdownController = new ShutdownController(logger, settings, text, uiFactory);
|
||||||
StartupController = new StartupController(logger, settings, text, uiFactory);
|
StartupController = new StartupController(logger, settings, text, uiFactory);
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ namespace SafeExamBrowser
|
||||||
StartupOperations.Enqueue(new KeyboardInterceptorOperation(keyboardInterceptor, logger, nativeMethods));
|
StartupOperations.Enqueue(new KeyboardInterceptorOperation(keyboardInterceptor, logger, nativeMethods));
|
||||||
StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
|
StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
|
||||||
StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
|
StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
|
||||||
StartupOperations.Enqueue(new WorkingAreaOperation(logger, Taskbar, workingArea));
|
StartupOperations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar));
|
||||||
StartupOperations.Enqueue(new TaskbarOperation(logger, logFormatter, settings, Taskbar, text, uiFactory));
|
StartupOperations.Enqueue(new TaskbarOperation(logger, logFormatter, settings, Taskbar, text, uiFactory));
|
||||||
StartupOperations.Enqueue(new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory));
|
StartupOperations.Enqueue(new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory));
|
||||||
StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger));
|
StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger));
|
||||||
|
|
Loading…
Reference in a new issue