SEBWIN-414: Implemented lock screen for user session switch.

This commit is contained in:
Damian Büchel 2020-06-29 19:29:48 +02:00
parent 44a7275f40
commit 9599102b9e
15 changed files with 391 additions and 49 deletions

View file

@ -27,6 +27,7 @@ using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.System;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
@ -59,6 +60,7 @@ namespace SafeExamBrowser.Client.UnitTests
private AppSettings settings;
private Mock<Action> shutdown;
private Mock<ISplashScreen> splashScreen;
private Mock<ISystemMonitor> systemMonitor;
private Mock<ITaskbar> taskbar;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
@ -86,6 +88,7 @@ namespace SafeExamBrowser.Client.UnitTests
settings = new AppSettings();
shutdown = new Mock<Action>();
splashScreen = new Mock<ISplashScreen>();
systemMonitor = new Mock<ISystemMonitor>();
taskbar = new Mock<ITaskbar>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
@ -108,6 +111,7 @@ namespace SafeExamBrowser.Client.UnitTests
runtimeProxy.Object,
shutdown.Object,
splashScreen.Object,
systemMonitor.Object,
taskbar.Object,
text.Object,
uiFactory.Object);

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 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 Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Operations;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.System;
namespace SafeExamBrowser.Client.UnitTests.Operations
{
[TestClass]
public class SystemMonitorOperationTests
{
private ClientContext context;
private Mock<ISystemMonitor> systemMonitor;
private Mock<ILogger> logger;
private SystemMonitorOperation sut;
[TestInitialize]
public void Initialize()
{
context = new ClientContext();
systemMonitor = new Mock<ISystemMonitor>();
logger = new Mock<ILogger>();
sut = new SystemMonitorOperation(context, systemMonitor.Object, logger.Object);
}
[TestMethod]
public void Perform_MustStartMonitor()
{
sut.Perform();
systemMonitor.Verify(s => s.Start(), Times.Once);
systemMonitor.VerifyNoOtherCalls();
}
[TestMethod]
public void Revert_MustStopMonitor()
{
sut.Revert();
systemMonitor.Verify(s => s.Stop(), Times.Once);
systemMonitor.VerifyNoOtherCalls();
}
}
}

View file

@ -95,6 +95,7 @@
<Compile Include="Communication\ClientHostTests.cs" />
<Compile Include="Notifications\AboutNotificationControllerTests.cs" />
<Compile Include="Notifications\LogNotificationControllerTests.cs" />
<Compile Include="Operations\SystemMonitorOperationTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ClientControllerTests.cs" />
</ItemGroup>

View file

@ -25,6 +25,7 @@ using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.System;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
@ -49,8 +50,10 @@ namespace SafeExamBrowser.Client
private IMessageBox messageBox;
private IOperationSequence operations;
private IRuntimeProxy runtime;
private bool sessionLocked;
private Action shutdown;
private ISplashScreen splashScreen;
private ISystemMonitor systemMonitor;
private ITaskbar taskbar;
private IText text;
private IUserInterfaceFactory uiFactory;
@ -73,6 +76,7 @@ namespace SafeExamBrowser.Client
IRuntimeProxy runtime,
Action shutdown,
ISplashScreen splashScreen,
ISystemMonitor systemMonitor,
ITaskbar taskbar,
IText text,
IUserInterfaceFactory uiFactory)
@ -90,6 +94,7 @@ namespace SafeExamBrowser.Client
this.runtime = runtime;
this.shutdown = shutdown;
this.splashScreen = splashScreen;
this.systemMonitor = systemMonitor;
this.taskbar = taskbar;
this.text = text;
this.uiFactory = uiFactory;
@ -184,6 +189,7 @@ namespace SafeExamBrowser.Client
ClientHost.Shutdown += ClientHost_Shutdown;
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
runtime.ConnectionLost += Runtime_ConnectionLost;
systemMonitor.SessionSwitched += SystemMonitor_SessionSwitched;
taskbar.QuitButtonClicked += Shell_QuitButtonClicked;
foreach (var activator in context.Activators.OfType<ITerminationActivator>())
@ -199,6 +205,7 @@ namespace SafeExamBrowser.Client
applicationMonitor.TerminationFailed -= ApplicationMonitor_TerminationFailed;
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
runtime.ConnectionLost -= Runtime_ConnectionLost;
systemMonitor.SessionSwitched -= SystemMonitor_SessionSwitched;
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
if (Browser != null)
@ -280,48 +287,14 @@ namespace SafeExamBrowser.Client
private void ApplicationMonitor_TerminationFailed(IEnumerable<RunningApplication> applications)
{
var applicationList = string.Join(Environment.NewLine, applications.Select(a => $"- {a.Name}"));
var message = $"{text.Get(TextKey.LockScreen_Message)}{Environment.NewLine}{Environment.NewLine}{applicationList}";
var message = $"{text.Get(TextKey.LockScreen_ApplicationsMessage)}{Environment.NewLine}{Environment.NewLine}{applicationList}";
var title = text.Get(TextKey.LockScreen_Title);
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
var allowOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_AllowOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_TerminateOption) };
var lockScreen = uiFactory.CreateLockScreen(message, title, new [] { allowOption, terminateOption });
var result = default(LockScreenResult);
var allowOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_ApplicationsAllowOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_ApplicationsTerminateOption) };
logger.Warn("Showing lock screen due to failed termination of blacklisted application(s)!");
PauseActivators();
lockScreen.Show();
logger.Warn("Detected termination failure of blacklisted application(s)!");
for (var unlocked = false; !unlocked;)
{
result = lockScreen.WaitForResult();
if (hasQuitPassword)
{
var passwordHash = hashAlgorithm.GenerateHashFor(result.Password);
var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase);
if (isCorrect)
{
logger.Info("The user entered the correct unlock password.");
unlocked = true;
}
else
{
logger.Info("The user entered the wrong unlock password.");
messageBox.Show(TextKey.MessageBox_InvalidUnlockPassword, TextKey.MessageBox_InvalidUnlockPasswordTitle, icon: MessageBoxIcon.Warning, parent: lockScreen);
}
}
else
{
logger.Warn($"No unlock password is defined, allowing user to resume session!");
unlocked = true;
}
}
lockScreen.Close();
ResumeActivators();
logger.Info("Closed lock screen.");
var result = ShowLockScreen(message, title, new[] { allowOption, terminateOption });
if (result.OptionId == allowOption.Id)
{
@ -524,6 +497,35 @@ namespace SafeExamBrowser.Client
ResumeActivators();
}
private void SystemMonitor_SessionSwitched()
{
var message = text.Get(TextKey.LockScreen_UserSessionMessage);
var title = text.Get(TextKey.LockScreen_Title);
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionContinueOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionTerminateOption) };
logger.Warn("Detected user session switch!");
if (!sessionLocked)
{
sessionLocked = true;
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
if (result.OptionId == terminateOption.Id)
{
logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
sessionLocked = false;
}
else
{
logger.Info("Lock screen is already active.");
}
}
private void TerminationActivator_Activated()
{
PauseActivators();
@ -599,6 +601,50 @@ namespace SafeExamBrowser.Client
}
}
private LockScreenResult ShowLockScreen(string message, string title, IEnumerable<LockScreenOption> options)
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
var lockScreen = uiFactory.CreateLockScreen(message, title, options);
var result = default(LockScreenResult);
logger.Info("Showing lock screen...");
PauseActivators();
lockScreen.Show();
for (var unlocked = false; !unlocked;)
{
result = lockScreen.WaitForResult();
if (hasQuitPassword)
{
var passwordHash = hashAlgorithm.GenerateHashFor(result.Password);
var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase);
if (isCorrect)
{
logger.Info("The user entered the correct unlock password.");
unlocked = true;
}
else
{
logger.Info("The user entered the wrong unlock password.");
messageBox.Show(TextKey.MessageBox_InvalidUnlockPassword, TextKey.MessageBox_InvalidUnlockPasswordTitle, icon: MessageBoxIcon.Warning, parent: lockScreen);
}
}
else
{
logger.Warn($"No unlock password is defined, allowing user to resume session!");
unlocked = true;
}
}
lockScreen.Close();
ResumeActivators();
logger.Info("Closed lock screen.");
return result;
}
private bool TryInitiateShutdown()
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);

View file

@ -30,6 +30,7 @@ using SafeExamBrowser.Monitoring.Applications;
using SafeExamBrowser.Monitoring.Display;
using SafeExamBrowser.Monitoring.Keyboard;
using SafeExamBrowser.Monitoring.Mouse;
using SafeExamBrowser.Monitoring.System;
using SafeExamBrowser.Settings.Logging;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.SystemComponents;
@ -100,6 +101,7 @@ namespace SafeExamBrowser.Client
var fileSystemDialog = BuildFileSystemDialog();
var hashAlgorithm = new HashAlgorithm();
var splashScreen = uiFactory.CreateSplashScreen();
var systemMonitor = new SystemMonitor(ModuleLogger(nameof(SystemMonitor)));
var operations = new Queue<IOperation>();
@ -113,6 +115,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new ApplicationOperation(context, applicationFactory, applicationMonitor, logger, text));
operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar));
operations.Enqueue(new SystemMonitorOperation(context, systemMonitor, logger));
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));
operations.Enqueue(new ClipboardOperation(context, logger, nativeMethods));
@ -133,6 +136,7 @@ namespace SafeExamBrowser.Client
runtimeProxy,
shutdown,
splashScreen,
systemMonitor,
taskbar,
text,
uiFactory);

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 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.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.System;
namespace SafeExamBrowser.Client.Operations
{
internal class SystemMonitorOperation : ClientOperation
{
private readonly ILogger logger;
private readonly ISystemMonitor systemMonitor;
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public override event StatusChangedEventHandler StatusChanged;
public SystemMonitorOperation(ClientContext context, ISystemMonitor systemMonitor, ILogger logger) : base(context)
{
this.logger = logger;
this.systemMonitor = systemMonitor;
}
public override OperationResult Perform()
{
logger.Info("Initializing system events...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeSystemEvents);
systemMonitor.Start();
return OperationResult.Success;
}
public override OperationResult Revert()
{
logger.Info("Finalizing system events...");
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeSystemEvents);
systemMonitor.Stop();
return OperationResult.Success;
}
}
}

View file

@ -95,6 +95,7 @@
<Compile Include="Operations\MouseInterceptorOperation.cs" />
<Compile Include="Operations\ApplicationOperation.cs" />
<Compile Include="Operations\ShellOperation.cs" />
<Compile Include="Operations\SystemMonitorOperation.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>

View file

@ -44,9 +44,12 @@ namespace SafeExamBrowser.I18n.Contracts
FileSystemDialog_Select,
FileSystemDialog_Title,
FolderDialog_ApplicationLocation,
LockScreen_AllowOption,
LockScreen_Message,
LockScreen_TerminateOption,
LockScreen_ApplicationsAllowOption,
LockScreen_ApplicationsMessage,
LockScreen_ApplicationsTerminateOption,
LockScreen_UserSessionContinueOption,
LockScreen_UserSessionMessage,
LockScreen_UserSessionTerminateOption,
LockScreen_Title,
LockScreen_UnlockButton,
LogWindow_AlwaysOnTop,
@ -117,13 +120,16 @@ namespace SafeExamBrowser.I18n.Contracts
OperationStatus_EmptyClipboard,
OperationStatus_FinalizeApplications,
OperationStatus_FinalizeServiceSession,
OperationStatus_FinalizeSystemEvents,
OperationStatus_InitializeApplications,
OperationStatus_InitializeBrowser,
OperationStatus_InitializeConfiguration,
OperationStatus_InitializeKioskMode,
OperationStatus_InitializeRuntimeConnection,
OperationStatus_InitializeServiceSession,
OperationStatus_InitializeSession,
OperationStatus_InitializeShell,
OperationStatus_InitializeSystemEvents,
OperationStatus_InitializeWorkingArea,
OperationStatus_RestartCommunicationHost,
OperationStatus_RestoreWorkingArea,
@ -132,7 +138,6 @@ namespace SafeExamBrowser.I18n.Contracts
OperationStatus_StartCommunicationHost,
OperationStatus_StartKeyboardInterception,
OperationStatus_StartMouseInterception,
OperationStatus_InitializeSession,
OperationStatus_StopClient,
OperationStatus_StopCommunicationHost,
OperationStatus_StopKeyboardInterception,

View file

@ -90,13 +90,13 @@
<Entry key="FolderDialog_ApplicationLocation">
Applikation "%%NAME%%" konnte nicht gefunden werden auf dem System! Bitte geben Sie an, wo die ausführbare Datei "%%EXECUTABLE%%" liegt.
</Entry>
<Entry key="LockScreen_AllowOption">
<Entry key="LockScreen_ApplicationsAllowOption">
Verbotene Applikationen temporär erlauben. Dies gilt nur für die momentan laufenden Instanzen der aktuellen Sitzung!
</Entry>
<Entry key="LockScreen_Message">
<Entry key="LockScreen_ApplicationsMessage">
Die unten aufgelisteten, verbotenen Applikationen wurden gestartet und konnten nicht automatisch beendet werden! Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren.
</Entry>
<Entry key="LockScreen_TerminateOption">
<Entry key="LockScreen_ApplicationsTerminateOption">
Safe Exam Browser beenden. WARNUNG: Sie werden keine Möglichkeit haben, Daten zu speichern oder weitere Aktionen auszuführen, SEB wird sofort beendet!
</Entry>
<Entry key="LockScreen_Title">
@ -105,6 +105,15 @@
<Entry key="LockScreen_UnlockButton">
Entsperren
</Entry>
<Entry key="LockScreen_UserSessionContinueOption">
Safe Exam Browser entsperren.
</Entry>
<Entry key="LockScreen_UserSessionMessage">
Der aktive Benutzer hat sich geändert oder der Computer wurde gesperrt! Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren.
</Entry>
<Entry key="LockScreen_UserSessionTerminateOption">
Safe Exam Browser beenden. WARNUNG: Sie werden keine Möglichkeit haben, Daten zu speichern oder weitere Aktionen auszuführen, SEB wird sofort beendet!
</Entry>
<Entry key="LogWindow_AlwaysOnTop">
Immer zuoberst
</Entry>
@ -309,6 +318,9 @@
<Entry key="OperationStatus_FinalizeServiceSession">
Beende Service-Sitzung
</Entry>
<Entry key="OperationStatus_FinalizeSystemEvents">
Finalisiere System-Ereignisse
</Entry>
<Entry key="OperationStatus_InitializeApplications">
Initialisiere Applikationen
</Entry>
@ -333,6 +345,9 @@
<Entry key="OperationStatus_InitializeShell">
Initialisiere Benutzeroberfläche
</Entry>
<Entry key="OperationStatus_InitializeSystemEvents">
Initialisiere System-Ereignisse
</Entry>
<Entry key="OperationStatus_InitializeWorkingArea">
Initialisiere Arbeitsbereich
</Entry>

View file

@ -90,13 +90,13 @@
<Entry key="FolderDialog_ApplicationLocation">
Application "%%NAME%%" could not be found on the system! Please locate the folder containing the main executable "%%EXECUTABLE%%".
</Entry>
<Entry key="LockScreen_AllowOption">
<Entry key="LockScreen_ApplicationsAllowOption">
Temporarily allow the blacklisted applications. This applies only to the currently running instances and session!
</Entry>
<Entry key="LockScreen_Message">
<Entry key="LockScreen_ApplicationsMessage">
The blacklisted applications listed below were started and could not be automatically terminated! In order to unlock SEB, please select one of the available options and enter the correct unlock password.
</Entry>
<Entry key="LockScreen_TerminateOption">
<Entry key="LockScreen_ApplicationsTerminateOption">
Terminate Safe Exam Browser. WARNING: There will be no possibility to save data or perform any further actions, the shutdown will be initiated immediately!
</Entry>
<Entry key="LockScreen_Title">
@ -105,6 +105,15 @@
<Entry key="LockScreen_UnlockButton">
Unlock
</Entry>
<Entry key="LockScreen_UserSessionContinueOption">
Unlock Safe Exam Browser.
</Entry>
<Entry key="LockScreen_UserSessionMessage">
The active user has changed or the computer has been locked! In order to unlock SEB, please select one of the available options and enter the correct unlock password.
</Entry>
<Entry key="LockScreen_UserSessionTerminateOption">
Terminate Safe Exam Browser. WARNING: There will be no possibility to save data or perform any further actions, the shutdown will be initiated immediately!
</Entry>
<Entry key="LogWindow_AlwaysOnTop">
Always on top
</Entry>
@ -309,6 +318,9 @@
<Entry key="OperationStatus_FinalizeServiceSession">
Finalizing service session
</Entry>
<Entry key="OperationStatus_FinalizeSystemEvents">
Finalizing system events
</Entry>
<Entry key="OperationStatus_InitializeApplications">
Initializing applications
</Entry>
@ -333,6 +345,9 @@
<Entry key="OperationStatus_InitializeShell">
Initializing user interface
</Entry>
<Entry key="OperationStatus_InitializeSystemEvents">
Initializing system events
</Entry>
<Entry key="OperationStatus_InitializeWorkingArea">
Initializing working area
</Entry>

View file

@ -65,6 +65,8 @@
<Compile Include="Applications\InitializationResult.cs" />
<Compile Include="Applications\IApplicationMonitor.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="System\Events\SessionSwitchedEventHandler.cs" />
<Compile Include="System\ISystemMonitor.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2020 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.Monitoring.Contracts.System.Events
{
/// <summary>
/// Indicates that the active user session of the operating system has changed.
/// </summary>
public delegate void SessionSwitchedEventHandler();
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2020 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.Monitoring.Contracts.System.Events;
namespace SafeExamBrowser.Monitoring.Contracts.System
{
/// <summary>
/// Monitors the operating system, e.g. for user account related events.
/// </summary>
public interface ISystemMonitor
{
/// <summary>
/// Event fired when the active user session has changed.
/// </summary>
event SessionSwitchedEventHandler SessionSwitched;
/// <summary>
/// Starts the monitoring.
/// </summary>
void Start();
/// <summary>
/// Stops the monitoring.
/// </summary>
void Stop();
}
}

View file

@ -63,6 +63,7 @@
<Compile Include="Mouse\MouseInterceptor.cs" />
<Compile Include="Applications\ApplicationMonitor.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="System\SystemMonitor.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2020 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.Threading.Tasks;
using Microsoft.Win32;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.System;
using SafeExamBrowser.Monitoring.Contracts.System.Events;
namespace SafeExamBrowser.Monitoring.System
{
public class SystemMonitor : ISystemMonitor
{
private readonly ILogger logger;
public event SessionSwitchedEventHandler SessionSwitched;
public SystemMonitor(ILogger logger)
{
this.logger = logger;
}
public void Start()
{
SystemEvents.EventsThreadShutdown += SystemEvents_EventsThreadShutdown;
SystemEvents.InstalledFontsChanged += SystemEvents_InstalledFontsChanged;
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
SystemEvents.SessionEnded += SystemEvents_SessionEnded;
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
SystemEvents.TimeChanged += SystemEvents_TimeChanged;
SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
logger.Info("Started monitoring the operating system.");
}
public void Stop()
{
SystemEvents.EventsThreadShutdown -= SystemEvents_EventsThreadShutdown;
SystemEvents.InstalledFontsChanged -= SystemEvents_InstalledFontsChanged;
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
SystemEvents.SessionEnded -= SystemEvents_SessionEnded;
SystemEvents.SessionEnding -= SystemEvents_SessionEnding;
SystemEvents.SessionSwitch -= SystemEvents_SessionSwitch;
SystemEvents.TimeChanged -= SystemEvents_TimeChanged;
SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
logger.Info("Stopped monitoring the operating system.");
}
private void SystemEvents_EventsThreadShutdown(object sender, EventArgs e)
{
logger.Warn("System event thread is about to be terminated!");
}
private void SystemEvents_InstalledFontsChanged(object sender, EventArgs e)
{
logger.Info("Installed fonts changed.");
}
private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
logger.Info($"Power mode changed: {e.Mode}");
}
private void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
logger.Warn($"User session ended! Reason: {e.Reason}");
}
private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
logger.Warn($"User session is ending! Reason: {e.Reason}");
}
private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
logger.Info($"User session switch detected! Reason: {e.Reason}");
Task.Run(() => SessionSwitched?.Invoke());
}
private void SystemEvents_TimeChanged(object sender, EventArgs e)
{
logger.Info("Time changed.");
}
private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
logger.Info($"User preference changed. Category: {e.Category}");
}
}
}