diff --git a/SafeExamBrowser.Core/Configuration/AboutNotificationIconResource.cs b/SafeExamBrowser.Configuration/AboutNotificationIconResource.cs
similarity index 93%
rename from SafeExamBrowser.Core/Configuration/AboutNotificationIconResource.cs
rename to SafeExamBrowser.Configuration/AboutNotificationIconResource.cs
index c0eefc69..43a52f3d 100644
--- a/SafeExamBrowser.Core/Configuration/AboutNotificationIconResource.cs
+++ b/SafeExamBrowser.Configuration/AboutNotificationIconResource.cs
@@ -9,7 +9,7 @@
using System;
using SafeExamBrowser.Contracts.Configuration;
-namespace SafeExamBrowser.Core.Configuration
+namespace SafeExamBrowser.Configuration
{
public class AboutNotificationIconResource : IIconResource
{
diff --git a/SafeExamBrowser.Core/Configuration/AboutNotificationInfo.cs b/SafeExamBrowser.Configuration/AboutNotificationInfo.cs
similarity index 93%
rename from SafeExamBrowser.Core/Configuration/AboutNotificationInfo.cs
rename to SafeExamBrowser.Configuration/AboutNotificationInfo.cs
index 2594fa57..e1f7dc11 100644
--- a/SafeExamBrowser.Core/Configuration/AboutNotificationInfo.cs
+++ b/SafeExamBrowser.Configuration/AboutNotificationInfo.cs
@@ -9,7 +9,7 @@
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
-namespace SafeExamBrowser.Core.Configuration
+namespace SafeExamBrowser.Configuration
{
public class AboutNotificationInfo : INotificationInfo
{
diff --git a/SafeExamBrowser.Configuration/Properties/AssemblyInfo.cs b/SafeExamBrowser.Configuration/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..7e30e8c0
--- /dev/null
+++ b/SafeExamBrowser.Configuration/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SafeExamBrowser.Configuration")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SafeExamBrowser.Configuration")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c388c4dd-a159-457d-af92-89f7ad185109")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj
new file mode 100644
index 00000000..4f3ea868
--- /dev/null
+++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj
@@ -0,0 +1,62 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {C388C4DD-A159-457D-AF92-89F7AD185109}
+ Library
+ Properties
+ SafeExamBrowser.Configuration
+ SafeExamBrowser.Configuration
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {47DA5933-BEF8-4729-94E6-ABDE2DB12262}
+ SafeExamBrowser.Contracts
+
+
+ {73724659-4150-4792-a94e-42f5f3c1b696}
+ SafeExamBrowser.WindowsApi
+
+
+
+
\ No newline at end of file
diff --git a/SafeExamBrowser.Core/Configuration/Settings.cs b/SafeExamBrowser.Configuration/Settings.cs
similarity index 96%
rename from SafeExamBrowser.Core/Configuration/Settings.cs
rename to SafeExamBrowser.Configuration/Settings.cs
index 56b5ae25..63370932 100644
--- a/SafeExamBrowser.Core/Configuration/Settings.cs
+++ b/SafeExamBrowser.Configuration/Settings.cs
@@ -11,7 +11,7 @@ using System.IO;
using System.Reflection;
using SafeExamBrowser.Contracts.Configuration;
-namespace SafeExamBrowser.Core.Configuration
+namespace SafeExamBrowser.Configuration
{
public class Settings : ISettings
{
diff --git a/SafeExamBrowser.Configuration/WorkingArea.cs b/SafeExamBrowser.Configuration/WorkingArea.cs
new file mode 100644
index 00000000..5c2ba20f
--- /dev/null
+++ b/SafeExamBrowser.Configuration/WorkingArea.cs
@@ -0,0 +1,61 @@
+/*
+ * 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.WindowsApi;
+using SafeExamBrowser.WindowsApi.Types;
+
+namespace SafeExamBrowser.Configuration
+{
+ public class WorkingArea : IWorkingArea
+ {
+ private ILogger logger;
+ private RECT? initial;
+
+ public WorkingArea(ILogger logger)
+ {
+ this.logger = logger;
+ }
+
+ public void InitializeFor(ITaskbar taskbar)
+ {
+ initial = User32.GetWorkingArea();
+
+ LogWorkingArea("Saved initial working area", initial.Value);
+
+ var area = new RECT
+ {
+ Left = 0,
+ Top = 0,
+ Right = Screen.PrimaryScreen.Bounds.Width,
+ Bottom = Screen.PrimaryScreen.Bounds.Height - taskbar.GetAbsoluteHeight()
+ };
+
+ LogWorkingArea("Setting new working area", area);
+ User32.SetWorkingArea(area);
+ LogWorkingArea("Working area is now set to", User32.GetWorkingArea());
+ }
+
+ public void Reset()
+ {
+ if (initial.HasValue)
+ {
+ User32.SetWorkingArea(initial.Value);
+ LogWorkingArea("Restored initial working area", initial.Value);
+ }
+ }
+
+ private void LogWorkingArea(string message, RECT area)
+ {
+ logger.Info($"{message}: Left = {area.Left}, Top = {area.Top}, Right = {area.Right}, Bottom = {area.Bottom}.");
+ }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Configuration/IWorkingArea.cs b/SafeExamBrowser.Contracts/Configuration/IWorkingArea.cs
new file mode 100644
index 00000000..2b61ce86
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Configuration/IWorkingArea.cs
@@ -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 SafeExamBrowser.Contracts.UserInterface;
+
+namespace SafeExamBrowser.Contracts.Configuration
+{
+ public interface IWorkingArea
+ {
+ ///
+ /// Sets the Windows working area to accommodate to the taskbar's dimensions.
+ ///
+ void InitializeFor(ITaskbar taskbar);
+
+ ///
+ /// Resets the Windows working area to its previous (initial) state.
+ ///
+ void Reset();
+ }
+}
diff --git a/SafeExamBrowser.Contracts/I18n/Key.cs b/SafeExamBrowser.Contracts/I18n/Key.cs
index 16c021fc..e8060f11 100644
--- a/SafeExamBrowser.Contracts/I18n/Key.cs
+++ b/SafeExamBrowser.Contracts/I18n/Key.cs
@@ -23,8 +23,8 @@ namespace SafeExamBrowser.Contracts.I18n
SplashScreen_InitializeBrowser,
SplashScreen_InitializeProcessMonitoring,
SplashScreen_InitializeTaskbar,
- SplashScreen_InitializeWorkArea,
- SplashScreen_RestoreWorkArea,
+ SplashScreen_InitializeWorkingArea,
+ SplashScreen_RestoreWorkingArea,
SplashScreen_ShutdownProcedure,
SplashScreen_StartupProcedure,
SplashScreen_StopProcessMonitoring,
diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
index 66ee8d3a..c28dffe8 100644
--- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
+++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
@@ -49,6 +49,7 @@
+
diff --git a/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs b/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs
index 5bcad222..88eff1ab 100644
--- a/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs
@@ -27,24 +27,15 @@ namespace SafeExamBrowser.Contracts.UserInterface
///
void SetMaxProgress(int max);
- ///
- /// Starts an animation indicating the user that something is going on.
- ///
- void StartBusyIndication();
-
- ///
- /// Stops the busy animation, if it was running.
- ///
- void StopBusyIndication();
-
///
/// Updates the progress bar of the splash screen according to the specified amount.
///
void UpdateProgress(int amount = 1);
///
- /// Updates the status text of the splash screen.
+ /// Updates the status text of the splash screen. If the busy flag is set,
+ /// the splash screen will show an animation to indicate a long-running operation.
///
- void UpdateText(Key key);
+ void UpdateText(Key key, bool showBusyIndication = false);
}
}
diff --git a/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs b/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs
index 973fb184..0a91efaa 100644
--- a/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs
@@ -22,13 +22,8 @@ namespace SafeExamBrowser.Contracts.UserInterface
void AddNotification(ITaskbarNotification button);
///
- /// Moves the taskbar to the given location on the screen.
+ /// Returns the absolute height of the taskbar (i.e. in physical pixels).
///
- void SetPosition(int x, int y);
-
- ///
- /// Sets the size of the taskbar.
- ///
- void SetSize(int width, int height);
+ int GetAbsoluteHeight();
}
}
diff --git a/SafeExamBrowser.Core/Behaviour/ShutdownController.cs b/SafeExamBrowser.Core/Behaviour/ShutdownController.cs
index fa2c3934..4870beec 100644
--- a/SafeExamBrowser.Core/Behaviour/ShutdownController.cs
+++ b/SafeExamBrowser.Core/Behaviour/ShutdownController.cs
@@ -28,13 +28,14 @@ namespace SafeExamBrowser.Core.Behaviour
private ISplashScreen splashScreen;
private IText text;
private IUiElementFactory uiFactory;
+ private IWorkingArea workingArea;
private IEnumerable ShutdownOperations
{
get
{
yield return StopProcessMonitoring;
- yield return RestoreWorkArea;
+ yield return RestoreWorkingArea;
yield return FinalizeApplicationLog;
}
}
@@ -45,7 +46,8 @@ namespace SafeExamBrowser.Core.Behaviour
IProcessMonitor processMonitor,
ISettings settings,
IText text,
- IUiElementFactory uiFactory)
+ IUiElementFactory uiFactory,
+ IWorkingArea workingArea)
{
this.logger = logger;
this.messageBox = messageBox;
@@ -53,6 +55,7 @@ namespace SafeExamBrowser.Core.Behaviour
this.settings = settings;
this.text = text;
this.uiFactory = uiFactory;
+ this.workingArea = workingArea;
}
public void FinalizeApplication()
@@ -83,11 +86,12 @@ namespace SafeExamBrowser.Core.Behaviour
splashScreen.SetMaxProgress(ShutdownOperations.Count());
splashScreen.UpdateText(Key.SplashScreen_ShutdownProcedure);
splashScreen.InvokeShow();
+ logger.Info("--- Initiating shutdown procedure ---");
}
private void StopProcessMonitoring()
{
- logger.Info("Stopping process monitoring.");
+ logger.Info("--- Stopping process monitoring ---");
splashScreen.UpdateText(Key.SplashScreen_StopProcessMonitoring);
// TODO
@@ -95,21 +99,22 @@ namespace SafeExamBrowser.Core.Behaviour
processMonitor.StopMonitoringExplorer();
}
- private void RestoreWorkArea()
+ private void RestoreWorkingArea()
{
- logger.Info("Restoring work area.");
- splashScreen.UpdateText(Key.SplashScreen_RestoreWorkArea);
+ logger.Info("--- Restoring working area ---");
+ splashScreen.UpdateText(Key.SplashScreen_RestoreWorkingArea);
// TODO
- splashScreen.UpdateText(Key.SplashScreen_WaitExplorerStartup);
- splashScreen.StartBusyIndication();
+ workingArea.Reset();
+
+ splashScreen.UpdateText(Key.SplashScreen_WaitExplorerStartup, true);
processMonitor.StartExplorerShell();
- splashScreen.StopBusyIndication();
}
private void FinalizeApplicationLog()
{
+ logger.Info("--- Application successfully finalized! ---");
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 5d698945..a842ce3f 100644
--- a/SafeExamBrowser.Core/Behaviour/StartupController.cs
+++ b/SafeExamBrowser.Core/Behaviour/StartupController.cs
@@ -32,6 +32,7 @@ namespace SafeExamBrowser.Core.Behaviour
private ITaskbar taskbar;
private IText text;
private IUiElementFactory uiFactory;
+ private IWorkingArea workingArea;
private IEnumerable StartupOperations
{
@@ -42,7 +43,7 @@ namespace SafeExamBrowser.Core.Behaviour
yield return EstablishWcfServiceConnection;
yield return DeactivateWindowsFeatures;
yield return InitializeProcessMonitoring;
- yield return InitializeWorkArea;
+ yield return InitializeWorkingArea;
yield return InitializeTaskbar;
yield return InitializeBrowser;
yield return FinishInitialization;
@@ -59,7 +60,8 @@ namespace SafeExamBrowser.Core.Behaviour
ISettings settings,
ITaskbar taskbar,
IText text,
- IUiElementFactory uiFactory)
+ IUiElementFactory uiFactory,
+ IWorkingArea workingArea)
{
this.browserController = browserController;
this.browserInfo = browserInfo;
@@ -71,6 +73,7 @@ namespace SafeExamBrowser.Core.Behaviour
this.taskbar = taskbar;
this.text = text;
this.uiFactory = uiFactory;
+ this.workingArea = workingArea;
}
public bool TryInitializeApplication()
@@ -109,7 +112,7 @@ namespace SafeExamBrowser.Core.Behaviour
logger.Log($"{titleLine}{copyrightLine}{emptyLine}{githubLine}");
logger.Log($"{Environment.NewLine}# Application started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}{Environment.NewLine}");
- logger.Info("Initiating startup procedure.");
+ logger.Info("--- Initiating startup procedure ---");
}
private void InitializeSplashScreen()
@@ -142,7 +145,7 @@ namespace SafeExamBrowser.Core.Behaviour
private void InitializeProcessMonitoring()
{
- logger.Info("Initializing process monitoring.");
+ logger.Info("--- Initializing process monitoring ---");
splashScreen.UpdateText(Key.SplashScreen_InitializeProcessMonitoring);
// TODO
@@ -150,24 +153,23 @@ namespace SafeExamBrowser.Core.Behaviour
processMonitor.StartMonitoringExplorer();
}
- private void InitializeWorkArea()
+ private void InitializeWorkingArea()
{
- logger.Info("Initializing work area.");
- splashScreen.UpdateText(Key.SplashScreen_InitializeWorkArea);
+ logger.Info("--- Initializing working area ---");
+ splashScreen.UpdateText(Key.SplashScreen_WaitExplorerTermination, true);
+ processMonitor.CloseExplorerShell();
// TODO
// - Minimizing all open windows
// - Emptying clipboard
- splashScreen.UpdateText(Key.SplashScreen_WaitExplorerTermination);
- splashScreen.StartBusyIndication();
- processMonitor.CloseExplorerShell();
- splashScreen.StopBusyIndication();
+ splashScreen.UpdateText(Key.SplashScreen_InitializeWorkingArea);
+ workingArea.InitializeFor(taskbar);
}
private void InitializeTaskbar()
{
- logger.Info("Initializing taskbar.");
+ logger.Info("--- Initializing taskbar ---");
splashScreen.UpdateText(Key.SplashScreen_InitializeTaskbar);
// TODO
@@ -179,7 +181,7 @@ namespace SafeExamBrowser.Core.Behaviour
private void InitializeBrowser()
{
- logger.Info("Initializing browser.");
+ logger.Info("--- Initializing browser ---");
splashScreen.UpdateText(Key.SplashScreen_InitializeBrowser);
// TODO
@@ -192,7 +194,7 @@ namespace SafeExamBrowser.Core.Behaviour
private void FinishInitialization()
{
- logger.Info("Application successfully initialized!");
+ logger.Info("--- Application successfully initialized! ---");
splashScreen.InvokeClose();
}
}
diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml
index 6629783b..4046706f 100644
--- a/SafeExamBrowser.Core/I18n/Text.xml
+++ b/SafeExamBrowser.Core/I18n/Text.xml
@@ -8,8 +8,8 @@
Initializing browser
Initializing process monitoring
Initializing taskbar
- Initializing work area
- Restoring work area
+ Initializing working area
+ Restoring working area
Initiating shutdown procedure
Initiating startup procedure
Stopping process monitoring
diff --git a/SafeExamBrowser.Core/Logging/ModuleLogger.cs b/SafeExamBrowser.Core/Logging/ModuleLogger.cs
new file mode 100644
index 00000000..558267d9
--- /dev/null
+++ b/SafeExamBrowser.Core/Logging/ModuleLogger.cs
@@ -0,0 +1,80 @@
+/*
+ * 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.Collections.Generic;
+using SafeExamBrowser.Contracts.Logging;
+
+namespace SafeExamBrowser.Core.Logging
+{
+ public class ModuleLogger : ILogger
+ {
+ private ILogger logger;
+ private Type module;
+
+ ///
+ /// Creates a wrapper around an ILogger that includes information
+ /// about the specified module when logging messages with a severity.
+ ///
+ public ModuleLogger(ILogger logger, Type module)
+ {
+ this.logger = logger;
+ this.module = module;
+ }
+
+ public void Error(string message)
+ {
+ logger.Error(AppendModuleInfo(message));
+ }
+
+ public void Error(string message, Exception exception)
+ {
+ logger.Error(AppendModuleInfo(message), exception);
+ }
+
+ public IList GetLog()
+ {
+ return logger.GetLog();
+ }
+
+ public void Info(string message)
+ {
+ logger.Info(AppendModuleInfo(message));
+ }
+
+ public void Log(string message)
+ {
+ logger.Log(message);
+ }
+
+ public void Log(ILogContent content)
+ {
+ logger.Log(content);
+ }
+
+ public void Subscribe(ILogObserver observer)
+ {
+ logger.Subscribe(observer);
+ }
+
+ public void Unsubscribe(ILogObserver observer)
+ {
+ logger.Unsubscribe(observer);
+ }
+
+ public void Warn(string message)
+ {
+ logger.Warn(AppendModuleInfo(message));
+ }
+
+ private string AppendModuleInfo(string message)
+ {
+ return $"[{module.Name}] {message}";
+ }
+ }
+}
diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
index a33eced1..db22d8c5 100644
--- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
+++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj
@@ -40,9 +40,6 @@
-
-
-
@@ -51,6 +48,7 @@
+
diff --git a/SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs b/SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs
index 2f50e75c..dc7cde93 100644
--- a/SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs
+++ b/SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs
@@ -13,6 +13,7 @@ using System.Linq;
using System.Threading;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
+using SafeExamBrowser.WindowsApi;
namespace SafeExamBrowser.Monitoring.Processes
{
@@ -28,9 +29,9 @@ namespace SafeExamBrowser.Monitoring.Processes
public void StartExplorerShell()
{
var process = new Process();
- var explorerPath = Path.Combine(Environment.GetEnvironmentVariable("WINDIR"), "explorer.exe");
+ var explorerPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe");
- Log("Restarting explorer shell...");
+ logger.Info("Restarting explorer shell...");
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = explorerPath;
@@ -42,7 +43,7 @@ namespace SafeExamBrowser.Monitoring.Processes
}
process.Refresh();
- Log($"Explorer shell successfully started with PID = {process.Id}.");
+ logger.Info($"Explorer shell successfully started with PID = {process.Id}.");
process.Close();
}
@@ -64,7 +65,7 @@ namespace SafeExamBrowser.Monitoring.Processes
if (shellProcess != null)
{
- Log($"Found explorer shell processes with PID = {processId}. Sending close message...");
+ logger.Info($"Found explorer shell processes with PID = {processId}. Sending close message...");
User32.PostCloseMessageToShell();
while (!shellProcess.HasExited)
@@ -73,17 +74,12 @@ namespace SafeExamBrowser.Monitoring.Processes
Thread.Sleep(20);
}
- Log($"Successfully terminated explorer shell process with PID = {processId}.");
+ logger.Info($"Successfully terminated explorer shell process with PID = {processId}.");
}
else
{
- Log("The explorer shell seems to already be terminated. Skipping this step...");
+ logger.Info("The explorer shell seems to already be terminated. Skipping this step...");
}
}
-
- private void Log(string message)
- {
- logger.Info($"[{nameof(ProcessMonitor)}] {message}");
- }
}
}
diff --git a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj
index d710e122..52b83b7b 100644
--- a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj
+++ b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj
@@ -42,13 +42,16 @@
-
{47da5933-bef8-4729-94e6-abde2db12262}
SafeExamBrowser.Contracts
+
+ {73724659-4150-4792-a94e-42f5f3c1b696}
+ SafeExamBrowser.WindowsApi
+
diff --git a/SafeExamBrowser.Monitoring/User32.cs b/SafeExamBrowser.Monitoring/User32.cs
deleted file mode 100644
index eca68cd4..00000000
--- a/SafeExamBrowser.Monitoring/User32.cs
+++ /dev/null
@@ -1,58 +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;
-using System.ComponentModel;
-using System.Runtime.InteropServices;
-
-namespace SafeExamBrowser.Monitoring
-{
- ///
- /// Provides access to the native Windows API exposed by user32.dll.
- ///
- internal static class User32
- {
- internal static IntPtr GetShellWindowHandle()
- {
- return FindWindow("Shell_TrayWnd", null);
- }
-
- internal static uint GetShellProcessId()
- {
- var handle = FindWindow("Shell_TrayWnd", null);
- var threadId = GetWindowThreadProcessId(handle, out uint processId);
-
- return processId;
- }
-
- ///
- /// The close message 0x5B4 posted to the shell is undocumented and not officially supported:
- /// https://stackoverflow.com/questions/5689904/gracefully-exit-explorer-programmatically/5705965#5705965
- ///
- internal static void PostCloseMessageToShell()
- {
- var taskbarHandle = FindWindow("Shell_TrayWnd", null);
- var success = PostMessage(taskbarHandle, 0x5B4, IntPtr.Zero, IntPtr.Zero);
-
- if (!success)
- {
- throw new Win32Exception(Marshal.GetLastWin32Error());
- }
- }
-
- [DllImport("user32.dll", SetLastError = true)]
- private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
-
- [DllImport("user32.dll", SetLastError = true)]
- private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
-
- [return: MarshalAs(UnmanagedType.Bool)]
- [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- private static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
- }
-}
diff --git a/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml
index 277fccb0..67808356 100644
--- a/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml
+++ b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml
@@ -27,10 +27,10 @@
-
+
-
+
diff --git a/SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml b/SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml
index 223aadbf..58d24d64 100644
--- a/SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml
+++ b/SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml
@@ -14,10 +14,10 @@
-
+
-
+
diff --git a/SafeExamBrowser.UserInterface/Controls/QuitButton.xaml b/SafeExamBrowser.UserInterface/Controls/QuitButton.xaml
index 75d33930..3a292f13 100644
--- a/SafeExamBrowser.UserInterface/Controls/QuitButton.xaml
+++ b/SafeExamBrowser.UserInterface/Controls/QuitButton.xaml
@@ -15,10 +15,10 @@
-
+
-
+
diff --git a/SafeExamBrowser.UserInterface/SplashScreen.xaml.cs b/SafeExamBrowser.UserInterface/SplashScreen.xaml.cs
index e6c03749..b78b85a3 100644
--- a/SafeExamBrowser.UserInterface/SplashScreen.xaml.cs
+++ b/SafeExamBrowser.UserInterface/SplashScreen.xaml.cs
@@ -45,24 +45,20 @@ namespace SafeExamBrowser.UserInterface
model.MaxProgress = max;
}
- public void StartBusyIndication()
- {
- model.StartBusyIndication();
- }
-
- public void StopBusyIndication()
- {
- model.StopBusyIndication();
- }
-
public void UpdateProgress(int amount = 1)
{
model.CurrentProgress += amount;
}
- public void UpdateText(Key key)
+ public void UpdateText(Key key, bool showBusyIndication = false)
{
+ model.StopBusyIndication();
model.Status = text.Get(key);
+
+ if (showBusyIndication)
+ {
+ model.StartBusyIndication();
+ }
}
private void InitializeSplashScreen()
diff --git a/SafeExamBrowser.UserInterface/Taskbar.xaml.cs b/SafeExamBrowser.UserInterface/Taskbar.xaml.cs
index 6ed2a950..3ece46dd 100644
--- a/SafeExamBrowser.UserInterface/Taskbar.xaml.cs
+++ b/SafeExamBrowser.UserInterface/Taskbar.xaml.cs
@@ -8,6 +8,8 @@
using System.Windows;
using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Media;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.UserInterface
@@ -17,6 +19,15 @@ namespace SafeExamBrowser.UserInterface
public Taskbar()
{
InitializeComponent();
+
+ Loaded += Taskbar_Loaded;
+ }
+
+ private void Taskbar_Loaded(object sender, RoutedEventArgs e)
+ {
+ Width = SystemParameters.WorkArea.Right;
+ Left = SystemParameters.WorkArea.Right - Width;
+ Top = SystemParameters.WorkArea.Bottom;
}
public void AddButton(ITaskbarButton button)
@@ -35,16 +46,28 @@ namespace SafeExamBrowser.UserInterface
}
}
- public void SetPosition(int x, int y)
+ public int GetAbsoluteHeight()
{
- Left = x;
- Top = y;
- }
+ // 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
- public void SetSize(int width, int height)
- {
- Width = width;
- Height = height;
+ 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 (int) transformToDevice.Transform((Vector) new Size(Width, Height)).Y;
}
private void ApplicationScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
diff --git a/SafeExamBrowser.WindowsApi/Constants/SPI.cs b/SafeExamBrowser.WindowsApi/Constants/SPI.cs
new file mode 100644
index 00000000..a64bd0d1
--- /dev/null
+++ b/SafeExamBrowser.WindowsApi/Constants/SPI.cs
@@ -0,0 +1,32 @@
+/*
+ * 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/SPI.html?diff=y.
+ ///
+ internal enum SPI : uint
+ {
+ ///
+ /// 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.
+ ///
+ SETWORKAREA = 0x002F,
+
+ ///
+ /// Retrieves the size of the work area on the primary display monitor. The work area is the portion of the screen
+ /// not obscured by the system taskbar or by application desktop toolbars. The pvParam parameter must point to a
+ /// 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.
+ ///
+ GETWORKAREA = 0x0030,
+ }
+}
diff --git a/SafeExamBrowser.WindowsApi/Constants/SPIF.cs b/SafeExamBrowser.WindowsApi/Constants/SPIF.cs
new file mode 100644
index 00000000..f62887d5
--- /dev/null
+++ b/SafeExamBrowser.WindowsApi/Constants/SPIF.cs
@@ -0,0 +1,36 @@
+/*
+ * 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.Constants
+{
+ ///
+ /// See http://www.pinvoke.net/default.aspx/Enums/SPIF.html.
+ ///
+ [Flags]
+ internal enum SPIF
+ {
+ NONE = 0x00,
+
+ ///
+ /// Writes the new system-wide parameter setting to the user profile.
+ ///
+ UPDATEINIFILE = 0x01,
+
+ ///
+ /// Broadcasts the WM_SETTINGCHANGE message after updating the user profile.
+ ///
+ SENDCHANGE = 0x02,
+
+ ///
+ /// Performs UPDATEINIFILE and SENDCHANGE.
+ ///
+ UPDATEANDCHANGE = 0x03
+ }
+}
diff --git a/SafeExamBrowser.WindowsApi/Properties/AssemblyInfo.cs b/SafeExamBrowser.WindowsApi/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..9f4bb183
--- /dev/null
+++ b/SafeExamBrowser.WindowsApi/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SafeExamBrowser.WindowsApi")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SafeExamBrowser.WindowsApi")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("73724659-4150-4792-a94e-42f5f3c1b696")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj
new file mode 100644
index 00000000..d7a99b2d
--- /dev/null
+++ b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj
@@ -0,0 +1,50 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {73724659-4150-4792-A94E-42F5F3C1B696}
+ Library
+ Properties
+ SafeExamBrowser.WindowsApi
+ SafeExamBrowser.WindowsApi
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SafeExamBrowser.WindowsApi/Types/RECT.cs b/SafeExamBrowser.WindowsApi/Types/RECT.cs
new file mode 100644
index 00000000..44077323
--- /dev/null
+++ b/SafeExamBrowser.WindowsApi/Types/RECT.cs
@@ -0,0 +1,24 @@
+/*
+ * 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.Runtime.InteropServices;
+
+namespace SafeExamBrowser.WindowsApi.Types
+{
+ ///
+ /// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd162897(v=vs.85).aspx.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RECT
+ {
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
+ }
+}
diff --git a/SafeExamBrowser.WindowsApi/User32.cs b/SafeExamBrowser.WindowsApi/User32.cs
new file mode 100644
index 00000000..f8e71093
--- /dev/null
+++ b/SafeExamBrowser.WindowsApi/User32.cs
@@ -0,0 +1,114 @@
+/*
+ * 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.ComponentModel;
+using System.Runtime.InteropServices;
+using SafeExamBrowser.WindowsApi.Constants;
+using SafeExamBrowser.WindowsApi.Types;
+
+namespace SafeExamBrowser.WindowsApi
+{
+ ///
+ /// Provides access to the native Windows API exposed by user32.dll.
+ ///
+ public static class User32
+ {
+ ///
+ /// 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).
+ ///
+ public static IntPtr GetShellWindowHandle()
+ {
+ return FindWindow("Shell_TrayWnd", null);
+ }
+
+ ///
+ /// 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 = FindWindow("Shell_TrayWnd", null);
+ var threadId = GetWindowThreadProcessId(handle, out uint processId);
+
+ return processId;
+ }
+
+ ///
+ /// Retrieves the currently configured working area of the primary screen.
+ ///
+ ///
+ /// If the working area could not be retrieved.
+ ///
+ public static RECT GetWorkingArea()
+ {
+ var workingArea = new RECT();
+ var success = SystemParametersInfo(SPI.GETWORKAREA, 0, ref workingArea, SPIF.NONE);
+
+ if (!success)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+ }
+
+ return workingArea;
+ }
+
+ ///
+ /// Instructs the main Windows explorer process to shut down.
+ ///
+ ///
+ /// If the messge could not be successfully posted. Does not apply if the process isn't running!
+ ///
+ ///
+ /// The close message 0x5B4 posted to the shell is undocumented and not officially supported:
+ /// https://stackoverflow.com/questions/5689904/gracefully-exit-explorer-programmatically/5705965#5705965
+ ///
+ public static void PostCloseMessageToShell()
+ {
+ var taskbarHandle = FindWindow("Shell_TrayWnd", null);
+ var success = PostMessage(taskbarHandle, 0x5B4, IntPtr.Zero, IntPtr.Zero);
+
+ if (!success)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+ }
+ }
+
+ ///
+ /// Sets the working area of the primary screen according to the given dimensions.
+ ///
+ ///
+ /// If the working area could not be set.
+ ///
+ public static void SetWorkingArea(RECT workingArea)
+ {
+ var success = SystemParametersInfo(SPI.SETWORKAREA, 0, ref workingArea, SPIF.UPDATEANDCHANGE);
+
+ if (!success)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+ }
+ }
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
+
+ [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", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref RECT pvParam, SPIF fWinIni);
+ }
+}
diff --git a/SafeExamBrowser.sln b/SafeExamBrowser.sln
index 70768e7c..1ad47d35 100644
--- a/SafeExamBrowser.sln
+++ b/SafeExamBrowser.sln
@@ -23,6 +23,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Browser", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Monitoring", "SafeExamBrowser.Monitoring\SafeExamBrowser.Monitoring.csproj", "{EF563531-4EB5-44B9-A5EC-D6D6F204469B}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Configuration", "SafeExamBrowser.Configuration\SafeExamBrowser.Configuration.csproj", "{C388C4DD-A159-457D-AF92-89F7AD185109}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.WindowsApi", "SafeExamBrowser.WindowsApi\SafeExamBrowser.WindowsApi.csproj", "{73724659-4150-4792-A94E-42F5F3C1B696}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -57,6 +61,14 @@ Global
{EF563531-4EB5-44B9-A5EC-D6D6F204469B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF563531-4EB5-44B9-A5EC-D6D6F204469B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF563531-4EB5-44B9-A5EC-D6D6F204469B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C388C4DD-A159-457D-AF92-89F7AD185109}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C388C4DD-A159-457D-AF92-89F7AD185109}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C388C4DD-A159-457D-AF92-89F7AD185109}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C388C4DD-A159-457D-AF92-89F7AD185109}.Release|Any CPU.Build.0 = Release|Any CPU
+ {73724659-4150-4792-A94E-42F5F3C1B696}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {73724659-4150-4792-A94E-42F5F3C1B696}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {73724659-4150-4792-A94E-42F5F3C1B696}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {73724659-4150-4792-A94E-42F5F3C1B696}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs
index 7b7af9fe..6b0cea9a 100644
--- a/SafeExamBrowser/CompositionRoot.cs
+++ b/SafeExamBrowser/CompositionRoot.cs
@@ -7,6 +7,7 @@
*/
using SafeExamBrowser.Browser;
+using SafeExamBrowser.Configuration;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
@@ -14,7 +15,6 @@ using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour;
-using SafeExamBrowser.Core.Configuration;
using SafeExamBrowser.Core.I18n;
using SafeExamBrowser.Core.Logging;
using SafeExamBrowser.Monitoring.Processes;
@@ -32,8 +32,9 @@ namespace SafeExamBrowser
private IProcessMonitor processMonitor;
private ISettings settings;
private IText text;
- private IUiElementFactory uiFactory;
private ITextResource textResource;
+ private IUiElementFactory uiFactory;
+ private IWorkingArea workingArea;
public IShutdownController ShutdownController { get; private set; }
public IStartupController StartupController { get; private set; }
@@ -54,9 +55,10 @@ namespace SafeExamBrowser
text = new Text(textResource);
aboutInfo = new AboutNotificationInfo(text);
- processMonitor = new ProcessMonitor(logger);
- ShutdownController = new ShutdownController(logger, messageBox, processMonitor, settings, text, uiFactory);
- StartupController = new StartupController(browserController, browserInfo, logger, messageBox, aboutInfo, processMonitor, settings, Taskbar, text, uiFactory);
+ processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)));
+ 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);
}
}
}
diff --git a/SafeExamBrowser/SafeExamBrowser.csproj b/SafeExamBrowser/SafeExamBrowser.csproj
index 044aa236..b65858a8 100644
--- a/SafeExamBrowser/SafeExamBrowser.csproj
+++ b/SafeExamBrowser/SafeExamBrowser.csproj
@@ -103,6 +103,10 @@
{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}
SafeExamBrowser.Browser
+
+ {c388c4dd-a159-457d-af92-89f7ad185109}
+ SafeExamBrowser.Configuration
+
{47DA5933-BEF8-4729-94E6-ABDE2DB12262}
SafeExamBrowser.Contracts