Created stub for process monitoring (explorer termination / restart) and enhanced startup and shutdown procedures.
This commit is contained in:
parent
72e8dcbb54
commit
a35fe0811f
25 changed files with 580 additions and 85 deletions
|
@ -21,7 +21,15 @@ namespace SafeExamBrowser.Contracts.I18n
|
|||
MessageBox_StartupErrorTitle,
|
||||
Notification_AboutTooltip,
|
||||
SplashScreen_InitializeBrowser,
|
||||
SplashScreen_InitializeProcessMonitoring,
|
||||
SplashScreen_InitializeTaskbar,
|
||||
SplashScreen_InitializeWorkArea,
|
||||
SplashScreen_RestoreWorkArea,
|
||||
SplashScreen_ShutdownProcedure,
|
||||
SplashScreen_StartupProcedure,
|
||||
SplashScreen_StopProcessMonitoring,
|
||||
SplashScreen_WaitExplorerStartup,
|
||||
SplashScreen_WaitExplorerTermination,
|
||||
Version
|
||||
}
|
||||
}
|
||||
|
|
14
SafeExamBrowser.Contracts/Monitoring/IKeyboardInterceptor.cs
Normal file
14
SafeExamBrowser.Contracts/Monitoring/IKeyboardInterceptor.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 interface IKeyboardInterceptor
|
||||
{
|
||||
}
|
||||
}
|
14
SafeExamBrowser.Contracts/Monitoring/IMouseInterceptor.cs
Normal file
14
SafeExamBrowser.Contracts/Monitoring/IMouseInterceptor.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 interface IMouseInterceptor
|
||||
{
|
||||
}
|
||||
}
|
34
SafeExamBrowser.Contracts/Monitoring/IProcessMonitor.cs
Normal file
34
SafeExamBrowser.Contracts/Monitoring/IProcessMonitor.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 interface IProcessMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts a new instance of the Windows explorer shell.
|
||||
/// </summary>
|
||||
void StartExplorerShell();
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring the Windows explorer, i.e. any newly created instances of
|
||||
/// <c>explorer.exe</c> will automatically be terminated.
|
||||
/// </summary>
|
||||
void StartMonitoringExplorer();
|
||||
|
||||
/// <summary>
|
||||
/// Stops monitoring the Windows explorer.
|
||||
/// </summary>
|
||||
void StopMonitoringExplorer();
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the Windows explorer shell, i.e. the taskbar.
|
||||
/// </summary>
|
||||
void CloseExplorerShell();
|
||||
}
|
||||
}
|
14
SafeExamBrowser.Contracts/Monitoring/IWindowMonitor.cs
Normal file
14
SafeExamBrowser.Contracts/Monitoring/IWindowMonitor.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 interface IWindowMonitor
|
||||
{
|
||||
}
|
||||
}
|
|
@ -58,6 +58,10 @@
|
|||
<Compile Include="Logging\ILogText.cs" />
|
||||
<Compile Include="Logging\IThreadInfo.cs" />
|
||||
<Compile Include="Logging\LogLevel.cs" />
|
||||
<Compile Include="Monitoring\IKeyboardInterceptor.cs" />
|
||||
<Compile Include="Monitoring\IMouseInterceptor.cs" />
|
||||
<Compile Include="Monitoring\IProcessMonitor.cs" />
|
||||
<Compile Include="Monitoring\IWindowMonitor.cs" />
|
||||
<Compile Include="UserInterface\IMessageBox.cs" />
|
||||
<Compile Include="UserInterface\ITaskbarNotification.cs" />
|
||||
<Compile Include="UserInterface\ISplashScreen.cs" />
|
||||
|
|
|
@ -13,9 +13,14 @@ namespace SafeExamBrowser.Contracts.UserInterface
|
|||
public interface ISplashScreen
|
||||
{
|
||||
/// <summary>
|
||||
/// Closes the splash screen.
|
||||
/// Closes the splash screen on its own thread.
|
||||
/// </summary>
|
||||
void Close();
|
||||
void InvokeClose();
|
||||
|
||||
/// <summary>
|
||||
/// Shows the splash screen on its own thread.
|
||||
/// </summary>
|
||||
void InvokeShow();
|
||||
|
||||
/// <summary>
|
||||
/// Set the maximum of the splash screen's progress bar.
|
||||
|
@ -23,9 +28,14 @@ namespace SafeExamBrowser.Contracts.UserInterface
|
|||
void SetMaxProgress(int max);
|
||||
|
||||
/// <summary>
|
||||
/// Shows the splash screen to the user.
|
||||
/// Starts an animation indicating the user that something is going on.
|
||||
/// </summary>
|
||||
void Show();
|
||||
void StartBusyIndication();
|
||||
|
||||
/// <summary>
|
||||
/// Stops the busy animation, if it was running.
|
||||
/// </summary>
|
||||
void StopBusyIndication();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the progress bar of the splash screen according to the specified amount.
|
||||
|
|
|
@ -30,10 +30,5 @@ namespace SafeExamBrowser.Contracts.UserInterface
|
|||
/// Sets the size of the taskbar.
|
||||
/// </summary>
|
||||
void SetSize(int width, int height);
|
||||
|
||||
/// <summary>
|
||||
/// Displays the taskbar on the screen.
|
||||
/// </summary>
|
||||
void Show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.UserInterface
|
||||
{
|
||||
|
@ -17,6 +18,11 @@ namespace SafeExamBrowser.Contracts.UserInterface
|
|||
/// </summary>
|
||||
ITaskbarButton CreateApplicationButton(IApplicationInfo info);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new splash screen which runs on its own thread.
|
||||
/// </summary>
|
||||
ISplashScreen CreateSplashScreen(ISettings settings, IText text);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a taskbar notification, initialized with the given notification information.
|
||||
/// </summary>
|
||||
|
|
|
@ -7,9 +7,14 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Contracts.Behaviour;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
|
||||
namespace SafeExamBrowser.Core.Behaviour
|
||||
|
@ -18,23 +23,52 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
{
|
||||
private ILogger logger;
|
||||
private IMessageBox messageBox;
|
||||
private IProcessMonitor processMonitor;
|
||||
private ISettings settings;
|
||||
private ISplashScreen splashScreen;
|
||||
private IText text;
|
||||
private IUiElementFactory uiFactory;
|
||||
|
||||
public ShutdownController(ILogger logger, IMessageBox messageBox, IText text)
|
||||
private IEnumerable<Action> ShutdownOperations
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return StopProcessMonitoring;
|
||||
yield return RestoreWorkArea;
|
||||
yield return FinalizeApplicationLog;
|
||||
}
|
||||
}
|
||||
|
||||
public ShutdownController(
|
||||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
IProcessMonitor processMonitor,
|
||||
ISettings settings,
|
||||
IText text,
|
||||
IUiElementFactory uiFactory)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.messageBox = messageBox;
|
||||
this.processMonitor = processMonitor;
|
||||
this.settings = settings;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public void FinalizeApplication()
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO:
|
||||
// - Gather TODOs!
|
||||
InitializeSplashScreen();
|
||||
|
||||
logger.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||
foreach (var operation in ShutdownOperations)
|
||||
{
|
||||
operation();
|
||||
splashScreen.UpdateProgress();
|
||||
|
||||
// TODO: Remove!
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -42,5 +76,41 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
messageBox.Show(text.Get(Key.MessageBox_ShutdownError), text.Get(Key.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeSplashScreen()
|
||||
{
|
||||
splashScreen = uiFactory.CreateSplashScreen(settings, text);
|
||||
splashScreen.SetMaxProgress(ShutdownOperations.Count());
|
||||
splashScreen.UpdateText(Key.SplashScreen_ShutdownProcedure);
|
||||
splashScreen.InvokeShow();
|
||||
}
|
||||
|
||||
private void StopProcessMonitoring()
|
||||
{
|
||||
logger.Info("Stopping process monitoring.");
|
||||
splashScreen.UpdateText(Key.SplashScreen_StopProcessMonitoring);
|
||||
|
||||
// TODO
|
||||
|
||||
processMonitor.StopMonitoringExplorer();
|
||||
}
|
||||
|
||||
private void RestoreWorkArea()
|
||||
{
|
||||
logger.Info("Restoring work area.");
|
||||
splashScreen.UpdateText(Key.SplashScreen_RestoreWorkArea);
|
||||
|
||||
// TODO
|
||||
|
||||
splashScreen.UpdateText(Key.SplashScreen_WaitExplorerStartup);
|
||||
splashScreen.StartBusyIndication();
|
||||
processMonitor.StartExplorerShell();
|
||||
splashScreen.StopBusyIndication();
|
||||
}
|
||||
|
||||
private void FinalizeApplicationLog()
|
||||
{
|
||||
logger.Log($"{Environment.NewLine}# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ using SafeExamBrowser.Contracts.Behaviour;
|
|||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
|
||||
namespace SafeExamBrowser.Core.Behaviour
|
||||
|
@ -25,6 +26,7 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
private ILogger logger;
|
||||
private IMessageBox messageBox;
|
||||
private INotificationInfo aboutInfo;
|
||||
private IProcessMonitor processMonitor;
|
||||
private ISettings settings;
|
||||
private ISplashScreen splashScreen;
|
||||
private ITaskbar taskbar;
|
||||
|
@ -53,8 +55,8 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
INotificationInfo aboutInfo,
|
||||
IProcessMonitor processMonitor,
|
||||
ISettings settings,
|
||||
ISplashScreen splashScreen,
|
||||
ITaskbar taskbar,
|
||||
IText text,
|
||||
IUiElementFactory uiFactory)
|
||||
|
@ -64,8 +66,8 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
this.logger = logger;
|
||||
this.messageBox = messageBox;
|
||||
this.aboutInfo = aboutInfo;
|
||||
this.processMonitor = processMonitor;
|
||||
this.settings = settings;
|
||||
this.splashScreen = splashScreen;
|
||||
this.taskbar = taskbar;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
|
@ -112,62 +114,63 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
|
||||
private void InitializeSplashScreen()
|
||||
{
|
||||
splashScreen = uiFactory.CreateSplashScreen(settings, text);
|
||||
splashScreen.SetMaxProgress(StartupOperations.Count());
|
||||
splashScreen.UpdateText(Key.SplashScreen_StartupProcedure);
|
||||
splashScreen.InvokeShow();
|
||||
}
|
||||
|
||||
private void HandleCommandLineArguments()
|
||||
{
|
||||
logger.Info("Parsing command line arguments.");
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void DetectOperatingSystem()
|
||||
{
|
||||
logger.Info("Detecting operating system.");
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void EstablishWcfServiceConnection()
|
||||
{
|
||||
logger.Info("Establishing connection to WCF service.");
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void DeactivateWindowsFeatures()
|
||||
{
|
||||
logger.Info("Deactivating Windows Update.");
|
||||
|
||||
// TODO
|
||||
|
||||
logger.Info("Disabling lock screen options.");
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void InitializeProcessMonitoring()
|
||||
{
|
||||
logger.Info("Initializing process monitoring.");
|
||||
splashScreen.UpdateText(Key.SplashScreen_InitializeProcessMonitoring);
|
||||
|
||||
// TODO
|
||||
|
||||
processMonitor.StartMonitoringExplorer();
|
||||
}
|
||||
|
||||
private void InitializeWorkArea()
|
||||
{
|
||||
logger.Info("Initializing work area.");
|
||||
splashScreen.UpdateText(Key.SplashScreen_InitializeWorkArea);
|
||||
|
||||
// TODO
|
||||
// - Killing explorer.exe
|
||||
// - Minimizing all open windows
|
||||
// - Emptying clipboard
|
||||
|
||||
splashScreen.UpdateText(Key.SplashScreen_WaitExplorerTermination);
|
||||
splashScreen.StartBusyIndication();
|
||||
processMonitor.CloseExplorerShell();
|
||||
splashScreen.StopBusyIndication();
|
||||
}
|
||||
|
||||
private void InitializeTaskbar()
|
||||
{
|
||||
logger.Info("Initializing taskbar.");
|
||||
splashScreen.UpdateText(Key.SplashScreen_InitializeTaskbar);
|
||||
|
||||
// TODO
|
||||
|
||||
var aboutNotification = uiFactory.CreateNotification(aboutInfo);
|
||||
|
||||
|
@ -179,6 +182,8 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
logger.Info("Initializing browser.");
|
||||
splashScreen.UpdateText(Key.SplashScreen_InitializeBrowser);
|
||||
|
||||
// TODO
|
||||
|
||||
var browserButton = uiFactory.CreateApplicationButton(browserInfo);
|
||||
|
||||
browserController.RegisterApplicationButton(browserButton);
|
||||
|
@ -188,6 +193,7 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
private void FinishInitialization()
|
||||
{
|
||||
logger.Info("Application successfully initialized!");
|
||||
splashScreen.InvokeClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,15 @@
|
|||
<MessageBox_StartupError>An unexpected error occurred during the startup procedure! Please consult the application log for more information...</MessageBox_StartupError>
|
||||
<MessageBox_StartupErrorTitle>Startup Error</MessageBox_StartupErrorTitle>
|
||||
<Notification_AboutTooltip>About Safe Exam Browser</Notification_AboutTooltip>
|
||||
<SplashScreen_InitializeBrowser>Initializing browser.</SplashScreen_InitializeBrowser>
|
||||
<SplashScreen_StartupProcedure>Initiating startup procedure.</SplashScreen_StartupProcedure>
|
||||
<SplashScreen_InitializeBrowser>Initializing browser</SplashScreen_InitializeBrowser>
|
||||
<SplashScreen_InitializeProcessMonitoring>Initializing process monitoring</SplashScreen_InitializeProcessMonitoring>
|
||||
<SplashScreen_InitializeTaskbar>Initializing taskbar</SplashScreen_InitializeTaskbar>
|
||||
<SplashScreen_InitializeWorkArea>Initializing work area</SplashScreen_InitializeWorkArea>
|
||||
<SplashScreen_RestoreWorkArea>Restoring work area</SplashScreen_RestoreWorkArea>
|
||||
<SplashScreen_ShutdownProcedure>Initiating shutdown procedure</SplashScreen_ShutdownProcedure>
|
||||
<SplashScreen_StartupProcedure>Initiating startup procedure</SplashScreen_StartupProcedure>
|
||||
<SplashScreen_StopProcessMonitoring>Stopping process monitoring</SplashScreen_StopProcessMonitoring>
|
||||
<SplashScreen_WaitExplorerStartup>Waiting for Windows explorer to start up</SplashScreen_WaitExplorerStartup>
|
||||
<SplashScreen_WaitExplorerTermination>Waiting for Windows explorer to shut down</SplashScreen_WaitExplorerTermination>
|
||||
<Version>Version</Version>
|
||||
</Text>
|
89
SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs
Normal file
89
SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.Processes
|
||||
{
|
||||
public class ProcessMonitor : IProcessMonitor
|
||||
{
|
||||
private ILogger logger;
|
||||
|
||||
public ProcessMonitor(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void StartExplorerShell()
|
||||
{
|
||||
var process = new Process();
|
||||
var explorerPath = Path.Combine(Environment.GetEnvironmentVariable("WINDIR"), "explorer.exe");
|
||||
|
||||
Log("Restarting explorer shell...");
|
||||
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.StartInfo.FileName = explorerPath;
|
||||
process.Start();
|
||||
|
||||
while (User32.GetShellWindowHandle() == IntPtr.Zero)
|
||||
{
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
process.Refresh();
|
||||
Log($"Explorer shell successfully started with PID = {process.Id}.");
|
||||
process.Close();
|
||||
}
|
||||
|
||||
public void StartMonitoringExplorer()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void StopMonitoringExplorer()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void CloseExplorerShell()
|
||||
{
|
||||
var processId = User32.GetShellProcessId();
|
||||
var explorerProcesses = Process.GetProcessesByName("explorer");
|
||||
var shellProcess = explorerProcesses.FirstOrDefault(p => p.Id == processId);
|
||||
|
||||
if (shellProcess != null)
|
||||
{
|
||||
Log($"Found explorer shell processes with PID = {processId}. Sending close message...");
|
||||
User32.PostCloseMessageToShell();
|
||||
|
||||
while (!shellProcess.HasExited)
|
||||
{
|
||||
shellProcess.Refresh();
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
Log($"Successfully terminated explorer shell process with PID = {processId}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("The explorer shell seems to already be terminated. Skipping this step...");
|
||||
}
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
logger.Info($"[{nameof(ProcessMonitor)}] {message}");
|
||||
}
|
||||
}
|
||||
}
|
36
SafeExamBrowser.Monitoring/Properties/AssemblyInfo.cs
Normal file
36
SafeExamBrowser.Monitoring/Properties/AssemblyInfo.cs
Normal file
|
@ -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.Monitoring")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SafeExamBrowser.Monitoring")]
|
||||
[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("ef563531-4eb5-44b9-a5ec-d6d6f204469b")]
|
||||
|
||||
// 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")]
|
59
SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj
Normal file
59
SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{EF563531-4EB5-44B9-A5EC-D6D6F204469B}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SafeExamBrowser.Monitoring</RootNamespace>
|
||||
<AssemblyName>SafeExamBrowser.Monitoring</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Processes\ProcessMonitor.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="User32.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj">
|
||||
<Project>{47da5933-bef8-4729-94e6-abde2db12262}</Project>
|
||||
<Name>SafeExamBrowser.Contracts</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Keyboard\" />
|
||||
<Folder Include="Mouse\" />
|
||||
<Folder Include="Windows\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
58
SafeExamBrowser.Monitoring/User32.cs
Normal file
58
SafeExamBrowser.Monitoring/User32.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the native Windows API exposed by <c>user32.dll</c>.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// The close message <c>0x5B4</c> posted to the shell is undocumented and not officially supported:
|
||||
/// https://stackoverflow.com/questions/5689904/gracefully-exit-explorer-programmatically/5705965#5705965
|
||||
/// </remarks>
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -58,7 +58,9 @@ namespace SafeExamBrowser.UserInterface.Controls
|
|||
Button.ToolTip = info.Tooltip;
|
||||
Button.Content = IconResourceLoader.Load(info.IconResource);
|
||||
|
||||
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1;
|
||||
Button.MouseLeave += (o, args) => InstancePopup.IsOpen &= InstancePopup.IsMouseOver || ActiveBar.IsMouseOver;
|
||||
ActiveBar.MouseLeave += (o, args) => InstancePopup.IsOpen &= InstancePopup.IsMouseOver || Button.IsMouseOver;
|
||||
InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = false;
|
||||
InstancePopup.Opened += (o, args) => ActiveBar.Width = Double.NaN;
|
||||
InstancePopup.Closed += (o, args) => ActiveBar.Width = 40;
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace SafeExamBrowser.UserInterface.Controls
|
|||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
Application.Current.MainWindow.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,11 +30,31 @@ namespace SafeExamBrowser.UserInterface
|
|||
InitializeSplashScreen();
|
||||
}
|
||||
|
||||
public void InvokeClose()
|
||||
{
|
||||
Dispatcher.Invoke(Close);
|
||||
}
|
||||
|
||||
public void InvokeShow()
|
||||
{
|
||||
Dispatcher.Invoke(Show);
|
||||
}
|
||||
|
||||
public void SetMaxProgress(int max)
|
||||
{
|
||||
model.MaxProgress = max;
|
||||
}
|
||||
|
||||
public void StartBusyIndication()
|
||||
{
|
||||
model.StartBusyIndication();
|
||||
}
|
||||
|
||||
public void StopBusyIndication()
|
||||
{
|
||||
model.StopBusyIndication();
|
||||
}
|
||||
|
||||
public void UpdateProgress(int amount = 1)
|
||||
{
|
||||
model.CurrentProgress += amount;
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
using SafeExamBrowser.UserInterface.Controls;
|
||||
|
||||
|
@ -19,6 +21,31 @@ namespace SafeExamBrowser.UserInterface
|
|||
return new ApplicationButton(info);
|
||||
}
|
||||
|
||||
public ISplashScreen CreateSplashScreen(ISettings settings, IText text)
|
||||
{
|
||||
SplashScreen splashScreen = null;
|
||||
var splashReadyEvent = new AutoResetEvent(false);
|
||||
var splashScreenThread = new Thread(() =>
|
||||
{
|
||||
splashScreen = new SplashScreen(settings, text);
|
||||
splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
|
||||
splashScreen.Show();
|
||||
|
||||
splashReadyEvent.Set();
|
||||
|
||||
System.Windows.Threading.Dispatcher.Run();
|
||||
});
|
||||
|
||||
splashScreenThread.SetApartmentState(ApartmentState.STA);
|
||||
splashScreenThread.Name = "Splash Screen Thread";
|
||||
splashScreenThread.IsBackground = true;
|
||||
splashScreenThread.Start();
|
||||
|
||||
splashReadyEvent.WaitOne();
|
||||
|
||||
return splashScreen;
|
||||
}
|
||||
|
||||
public ITaskbarNotification CreateNotification(INotificationInfo info)
|
||||
{
|
||||
return new NotificationIcon(info);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Timers;
|
||||
|
||||
namespace SafeExamBrowser.UserInterface.ViewModels
|
||||
{
|
||||
|
@ -15,6 +16,7 @@ namespace SafeExamBrowser.UserInterface.ViewModels
|
|||
private int currentProgress;
|
||||
private int maxProgress;
|
||||
private string status;
|
||||
private Timer busyTimer;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
|
@ -56,5 +58,41 @@ namespace SafeExamBrowser.UserInterface.ViewModels
|
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Status)));
|
||||
}
|
||||
}
|
||||
|
||||
public void StartBusyIndication()
|
||||
{
|
||||
StopBusyIndication();
|
||||
|
||||
busyTimer = new Timer
|
||||
{
|
||||
AutoReset = true,
|
||||
Interval = 750
|
||||
};
|
||||
|
||||
busyTimer.Elapsed += BusyTimer_Elapsed;
|
||||
busyTimer.Start();
|
||||
}
|
||||
|
||||
public void StopBusyIndication()
|
||||
{
|
||||
busyTimer?.Stop();
|
||||
busyTimer?.Close();
|
||||
}
|
||||
|
||||
private void BusyTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var next = Status ?? string.Empty;
|
||||
|
||||
if (next.EndsWith("..."))
|
||||
{
|
||||
next = Status.Substring(0, Status.Length - 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
next += ".";
|
||||
}
|
||||
|
||||
Status = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Contracts",
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Browser", "SafeExamBrowser.Browser\SafeExamBrowser.Browser.csproj", "{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Monitoring", "SafeExamBrowser.Monitoring\SafeExamBrowser.Monitoring.csproj", "{EF563531-4EB5-44B9-A5EC-D6D6F204469B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -51,6 +53,10 @@ Global
|
|||
{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04E653F1-98E6-4E34-9DD7-7F2BC1A8B767}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF563531-4EB5-44B9-A5EC-D6D6F204469B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -26,7 +26,11 @@ namespace SafeExamBrowser
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show(e.Message + "\n\n" + e.StackTrace, "Fatal Error");
|
||||
MessageBox.Show(e.Message + "\n\n" + e.StackTrace, "Fatal Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mutex.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +42,7 @@ namespace SafeExamBrowser
|
|||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("You can only run one instance of SEB at a time.", "Startup Not Allowed");
|
||||
MessageBox.Show("You can only run one instance of SEB at a time.", "Startup Not Allowed", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,43 +55,6 @@ namespace SafeExamBrowser
|
|||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
ShowSplashScreen();
|
||||
InitializeApplication();
|
||||
}
|
||||
|
||||
protected override void OnExit(ExitEventArgs e)
|
||||
{
|
||||
instances.ShutdownController.FinalizeApplication();
|
||||
|
||||
base.OnExit(e);
|
||||
}
|
||||
|
||||
private void ShowSplashScreen()
|
||||
{
|
||||
instances.BuildModulesRequiredBySplashScreen();
|
||||
|
||||
var splashReadyEvent = new AutoResetEvent(false);
|
||||
var splashScreenThread = new Thread(() =>
|
||||
{
|
||||
instances.SplashScreen = new UserInterface.SplashScreen(instances.Settings, instances.Text);
|
||||
instances.SplashScreen.Closed += (o, args) => instances.SplashScreen.Dispatcher.InvokeShutdown();
|
||||
instances.SplashScreen.Show();
|
||||
|
||||
splashReadyEvent.Set();
|
||||
|
||||
System.Windows.Threading.Dispatcher.Run();
|
||||
});
|
||||
|
||||
splashScreenThread.SetApartmentState(ApartmentState.STA);
|
||||
splashScreenThread.Name = "Splash Screen Thread";
|
||||
splashScreenThread.IsBackground = true;
|
||||
splashScreenThread.Start();
|
||||
|
||||
splashReadyEvent.WaitOne();
|
||||
}
|
||||
|
||||
private void InitializeApplication()
|
||||
{
|
||||
instances.BuildObjectGraph();
|
||||
|
||||
var success = instances.StartupController.TryInitializeApplication();
|
||||
|
@ -95,14 +62,19 @@ namespace SafeExamBrowser
|
|||
if (success)
|
||||
{
|
||||
MainWindow = instances.Taskbar;
|
||||
MainWindow.Closing += (o, args) => ShutdownApplication();
|
||||
MainWindow.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
instances.SplashScreen?.Dispatcher.InvokeAsync(instances.SplashScreen.Close);
|
||||
private void ShutdownApplication()
|
||||
{
|
||||
MainWindow.Hide();
|
||||
instances.ShutdownController.FinalizeApplication();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,13 @@ using SafeExamBrowser.Contracts.Behaviour;
|
|||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
using SafeExamBrowser.Core.Behaviour;
|
||||
using SafeExamBrowser.Core.Configuration;
|
||||
using SafeExamBrowser.Core.I18n;
|
||||
using SafeExamBrowser.Core.Logging;
|
||||
using SafeExamBrowser.Monitoring.Processes;
|
||||
using SafeExamBrowser.UserInterface;
|
||||
|
||||
namespace SafeExamBrowser
|
||||
|
@ -24,23 +26,18 @@ namespace SafeExamBrowser
|
|||
{
|
||||
private IApplicationController browserController;
|
||||
private IApplicationInfo browserInfo;
|
||||
private ILogger logger;
|
||||
private IMessageBox messageBox;
|
||||
private INotificationInfo aboutInfo;
|
||||
private ILogger logger;
|
||||
private IProcessMonitor processMonitor;
|
||||
private ISettings settings;
|
||||
private IText text;
|
||||
private IUiElementFactory uiFactory;
|
||||
private ITextResource textResource;
|
||||
|
||||
public ISettings Settings { get; private set; }
|
||||
public IShutdownController ShutdownController { get; private set; }
|
||||
public IStartupController StartupController { get; private set; }
|
||||
public SplashScreen SplashScreen { get; set; }
|
||||
public Taskbar Taskbar { get; private set; }
|
||||
public IText Text { get; private set; }
|
||||
|
||||
public void BuildModulesRequiredBySplashScreen()
|
||||
{
|
||||
Settings = new Settings();
|
||||
Text = new Text(new XmlTextResource());
|
||||
}
|
||||
|
||||
public void BuildObjectGraph()
|
||||
{
|
||||
|
@ -48,14 +45,18 @@ namespace SafeExamBrowser
|
|||
browserInfo = new BrowserApplicationInfo();
|
||||
logger = new Logger();
|
||||
messageBox = new WpfMessageBox();
|
||||
settings = new Settings();
|
||||
Taskbar = new Taskbar();
|
||||
textResource = new XmlTextResource();
|
||||
uiFactory = new UiElementFactory();
|
||||
|
||||
logger.Subscribe(new LogFileWriter(Settings));
|
||||
logger.Subscribe(new LogFileWriter(settings));
|
||||
|
||||
aboutInfo = new AboutNotificationInfo(Text);
|
||||
ShutdownController = new ShutdownController(logger, messageBox, Text);
|
||||
StartupController = new StartupController(browserController, browserInfo, logger, messageBox, aboutInfo, Settings, SplashScreen, Taskbar, Text, uiFactory);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,10 @@
|
|||
<Project>{3d6fdbb6-a4af-4626-bb2b-bf329d44f9cc}</Project>
|
||||
<Name>SafeExamBrowser.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Monitoring\SafeExamBrowser.Monitoring.csproj">
|
||||
<Project>{ef563531-4eb5-44b9-a5ec-d6d6f204469b}</Project>
|
||||
<Name>SafeExamBrowser.Monitoring</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.UserInterface\SafeExamBrowser.UserInterface.csproj">
|
||||
<Project>{e1be031a-4354-41e7-83e8-843ded4489ff}</Project>
|
||||
<Name>SafeExamBrowser.UserInterface</Name>
|
||||
|
|
Loading…
Reference in a new issue