SEBWIN-226: Implemented termination activator for global CTRL+Q keyboard shortcut.

This commit is contained in:
dbuechel 2019-03-29 07:46:21 +01:00
parent 25022295e1
commit 719411d8e3
20 changed files with 280 additions and 49 deletions

View file

@ -49,6 +49,7 @@ namespace SafeExamBrowser.Client.UnitTests
private Settings settings; private Settings settings;
private Mock<Action> shutdown; private Mock<Action> shutdown;
private Mock<ITaskbar> taskbar; private Mock<ITaskbar> taskbar;
private Mock<ITerminationActivator> terminationActivator;
private Mock<IText> text; private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory; private Mock<IUserInterfaceFactory> uiFactory;
private Mock<IWindowMonitor> windowMonitor; private Mock<IWindowMonitor> windowMonitor;
@ -74,6 +75,7 @@ namespace SafeExamBrowser.Client.UnitTests
settings = new Settings(); settings = new Settings();
shutdown = new Mock<Action>(); shutdown = new Mock<Action>();
taskbar = new Mock<ITaskbar>(); taskbar = new Mock<ITaskbar>();
terminationActivator = new Mock<ITerminationActivator>();
text = new Mock<IText>(); text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>(); uiFactory = new Mock<IUserInterfaceFactory>();
windowMonitor = new Mock<IWindowMonitor>(); windowMonitor = new Mock<IWindowMonitor>();
@ -94,6 +96,7 @@ namespace SafeExamBrowser.Client.UnitTests
runtimeProxy.Object, runtimeProxy.Object,
shutdown.Object, shutdown.Object,
taskbar.Object, taskbar.Object,
terminationActivator.Object,
text.Object, text.Object,
uiFactory.Object, uiFactory.Object,
windowMonitor.Object); windowMonitor.Object);

View file

@ -17,6 +17,7 @@ using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Shell; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.WindowsApi;
namespace SafeExamBrowser.Client.UnitTests.Operations namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
@ -28,6 +29,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
private ActionCenterSettings actionCenterSettings; private ActionCenterSettings actionCenterSettings;
private Mock<ILogger> logger; private Mock<ILogger> logger;
private TaskbarSettings taskbarSettings; private TaskbarSettings taskbarSettings;
private Mock<ITerminationActivator> terminationActivator;
private Mock<INotificationInfo> aboutInfo; private Mock<INotificationInfo> aboutInfo;
private Mock<INotificationController> aboutController; private Mock<INotificationController> aboutController;
private Mock<INotificationInfo> logInfo; private Mock<INotificationInfo> logInfo;
@ -59,6 +61,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
systemInfo = new Mock<ISystemInfo>(); systemInfo = new Mock<ISystemInfo>();
taskbar = new Mock<ITaskbar>(); taskbar = new Mock<ITaskbar>();
taskbarSettings = new TaskbarSettings(); taskbarSettings = new TaskbarSettings();
terminationActivator = new Mock<ITerminationActivator>();
text = new Mock<IText>(); text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>(); uiFactory = new Mock<IUserInterfaceFactory>();
@ -84,6 +87,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
systemInfo.Object, systemInfo.Object,
taskbar.Object, taskbar.Object,
taskbarSettings, taskbarSettings,
terminationActivator.Object,
text.Object, text.Object,
uiFactory.Object); uiFactory.Object);
} }

View file

@ -44,6 +44,7 @@ namespace SafeExamBrowser.Client
private Action shutdown; private Action shutdown;
private ISplashScreen splashScreen; private ISplashScreen splashScreen;
private ITaskbar taskbar; private ITaskbar taskbar;
private ITerminationActivator terminationActivator;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor; private IWindowMonitor windowMonitor;
@ -79,6 +80,7 @@ namespace SafeExamBrowser.Client
IRuntimeProxy runtime, IRuntimeProxy runtime,
Action shutdown, Action shutdown,
ITaskbar taskbar, ITaskbar taskbar,
ITerminationActivator terminationActivator,
IText text, IText text,
IUserInterfaceFactory uiFactory, IUserInterfaceFactory uiFactory,
IWindowMonitor windowMonitor) IWindowMonitor windowMonitor)
@ -94,6 +96,7 @@ namespace SafeExamBrowser.Client
this.runtime = runtime; this.runtime = runtime;
this.shutdown = shutdown; this.shutdown = shutdown;
this.taskbar = taskbar; this.taskbar = taskbar;
this.terminationActivator = terminationActivator;
this.text = text; this.text = text;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
this.windowMonitor = windowMonitor; this.windowMonitor = windowMonitor;
@ -178,6 +181,7 @@ namespace SafeExamBrowser.Client
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted; processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
runtime.ConnectionLost += Runtime_ConnectionLost; runtime.ConnectionLost += Runtime_ConnectionLost;
taskbar.QuitButtonClicked += Shell_QuitButtonClicked; taskbar.QuitButtonClicked += Shell_QuitButtonClicked;
terminationActivator.Activated += TerminationActivator_Activated;
windowMonitor.WindowChanged += WindowMonitor_WindowChanged; windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
} }
@ -188,6 +192,7 @@ namespace SafeExamBrowser.Client
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted; processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
runtime.ConnectionLost -= Runtime_ConnectionLost; runtime.ConnectionLost -= Runtime_ConnectionLost;
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked; taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
terminationActivator.Activated -= TerminationActivator_Activated;
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged; windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
if (Browser != null) if (Browser != null)
@ -383,6 +388,35 @@ namespace SafeExamBrowser.Client
} }
private void Shell_QuitButtonClicked(System.ComponentModel.CancelEventArgs args) private void Shell_QuitButtonClicked(System.ComponentModel.CancelEventArgs args)
{
terminationActivator.Pause();
args.Cancel = !TryInitiateShutdown();
terminationActivator.Resume();
}
private void TerminationActivator_Activated()
{
terminationActivator.Pause();
TryInitiateShutdown();
terminationActivator.Resume();
}
private void WindowMonitor_WindowChanged(IntPtr window)
{
var allowed = processMonitor.BelongsToAllowedProcess(window);
if (!allowed)
{
var success = windowMonitor.Hide(window);
if (!success)
{
windowMonitor.Close(window);
}
}
}
private bool TryInitiateShutdown()
{ {
var hasQuitPassword = !String.IsNullOrEmpty(Settings.QuitPasswordHash); var hasQuitPassword = !String.IsNullOrEmpty(Settings.QuitPasswordHash);
var requestShutdown = false; var requestShutdown = false;
@ -400,31 +434,18 @@ namespace SafeExamBrowser.Client
{ {
var communication = runtime.RequestShutdown(); var communication = runtime.RequestShutdown();
if (!communication.Success) if (communication.Success)
{
return true;
}
else
{ {
logger.Error("Failed to communicate shutdown request to the runtime!"); logger.Error("Failed to communicate shutdown request to the runtime!");
messageBox.Show(TextKey.MessageBox_QuitError, TextKey.MessageBox_QuitErrorTitle, icon: MessageBoxIcon.Error); messageBox.Show(TextKey.MessageBox_QuitError, TextKey.MessageBox_QuitErrorTitle, icon: MessageBoxIcon.Error);
} }
} }
else
{
args.Cancel = true;
}
}
private void WindowMonitor_WindowChanged(IntPtr window) return false;
{
var allowed = processMonitor.BelongsToAllowedProcess(window);
if (!allowed)
{
var success = windowMonitor.Hide(window);
if (!success)
{
windowMonitor.Close(window);
}
}
} }
private bool TryConfirmShutdown() private bool TryConfirmShutdown()

View file

@ -71,6 +71,7 @@ namespace SafeExamBrowser.Client
private ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork; private ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork;
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
private ITaskbar taskbar; private ITaskbar taskbar;
private ITerminationActivator terminationActivator;
private IText text; private IText text;
private ITextResource textResource; private ITextResource textResource;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
@ -98,6 +99,7 @@ namespace SafeExamBrowser.Client
uiFactory = BuildUserInterfaceFactory(); uiFactory = BuildUserInterfaceFactory();
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy))); runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)));
taskbar = BuildTaskbar(); taskbar = BuildTaskbar();
terminationActivator = new TerminationActivator(new ModuleLogger(logger, nameof(TerminationActivator)));
windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods); windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods);
wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, nameof(WirelessNetwork)), text); wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, nameof(WirelessNetwork)), text);
@ -137,6 +139,7 @@ namespace SafeExamBrowser.Client
runtimeProxy, runtimeProxy,
shutdown, shutdown,
taskbar, taskbar,
terminationActivator,
text, text,
uiFactory, uiFactory,
windowMonitor); windowMonitor);
@ -279,6 +282,7 @@ namespace SafeExamBrowser.Client
systemInfo, systemInfo,
taskbar, taskbar,
configuration.Settings.Taskbar, configuration.Settings.Taskbar,
terminationActivator,
text, text,
uiFactory); uiFactory);

View file

@ -16,6 +16,7 @@ using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Shell; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.WindowsApi;
namespace SafeExamBrowser.Client.Operations namespace SafeExamBrowser.Client.Operations
{ {
@ -35,6 +36,7 @@ namespace SafeExamBrowser.Client.Operations
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
private ITaskbar taskbar; private ITaskbar taskbar;
private TaskbarSettings taskbarSettings; private TaskbarSettings taskbarSettings;
private ITerminationActivator terminationActivator;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
@ -56,6 +58,7 @@ namespace SafeExamBrowser.Client.Operations
ISystemInfo systemInfo, ISystemInfo systemInfo,
ITaskbar taskbar, ITaskbar taskbar,
TaskbarSettings taskbarSettings, TaskbarSettings taskbarSettings,
ITerminationActivator terminationActivator,
IText text, IText text,
IUserInterfaceFactory uiFactory) IUserInterfaceFactory uiFactory)
{ {
@ -71,6 +74,7 @@ namespace SafeExamBrowser.Client.Operations
this.powerSupply = powerSupply; this.powerSupply = powerSupply;
this.systemInfo = systemInfo; this.systemInfo = systemInfo;
this.taskbarSettings = taskbarSettings; this.taskbarSettings = taskbarSettings;
this.terminationActivator = terminationActivator;
this.text = text; this.text = text;
this.taskbar = taskbar; this.taskbar = taskbar;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
@ -85,6 +89,7 @@ namespace SafeExamBrowser.Client.Operations
InitializeSystemComponents(); InitializeSystemComponents();
InitializeActionCenter(); InitializeActionCenter();
InitializeTaskbar(); InitializeTaskbar();
InitializeActivators();
return OperationResult.Success; return OperationResult.Success;
} }
@ -101,6 +106,11 @@ namespace SafeExamBrowser.Client.Operations
return OperationResult.Success; return OperationResult.Success;
} }
private void InitializeActivators()
{
terminationActivator.Start();
}
private void InitializeActionCenter() private void InitializeActionCenter()
{ {
if (actionCenterSettings.EnableActionCenter) if (actionCenterSettings.EnableActionCenter)
@ -270,6 +280,8 @@ namespace SafeExamBrowser.Client.Operations
private void TerminateActivators() private void TerminateActivators()
{ {
terminationActivator.Stop();
if (actionCenterSettings.EnableActionCenter) if (actionCenterSettings.EnableActionCenter)
{ {
foreach (var activator in activators) foreach (var activator in activators)

View file

@ -218,6 +218,7 @@
<Compile Include="UserInterface\MessageBox\MessageBoxIcon.cs" /> <Compile Include="UserInterface\MessageBox\MessageBoxIcon.cs" />
<Compile Include="WindowsApi\Events\ProcessTerminatedEventHandler.cs" /> <Compile Include="WindowsApi\Events\ProcessTerminatedEventHandler.cs" />
<Compile Include="WindowsApi\Events\SystemEventCallback.cs" /> <Compile Include="WindowsApi\Events\SystemEventCallback.cs" />
<Compile Include="WindowsApi\Events\TerminationActivatorEventHandler.cs" />
<Compile Include="WindowsApi\IBounds.cs" /> <Compile Include="WindowsApi\IBounds.cs" />
<Compile Include="WindowsApi\IDesktop.cs" /> <Compile Include="WindowsApi\IDesktop.cs" />
<Compile Include="WindowsApi\IDesktopFactory.cs" /> <Compile Include="WindowsApi\IDesktopFactory.cs" />
@ -225,6 +226,7 @@
<Compile Include="WindowsApi\INativeMethods.cs" /> <Compile Include="WindowsApi\INativeMethods.cs" />
<Compile Include="WindowsApi\IProcess.cs" /> <Compile Include="WindowsApi\IProcess.cs" />
<Compile Include="WindowsApi\IProcessFactory.cs" /> <Compile Include="WindowsApi\IProcessFactory.cs" />
<Compile Include="WindowsApi\ITerminationActivator.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View file

@ -18,17 +18,17 @@ namespace SafeExamBrowser.Contracts.UserInterface.Shell
/// <summary> /// <summary>
/// Fired when the action center should be made visible. /// Fired when the action center should be made visible.
/// </summary> /// </summary>
event ActivatorEventHandler Activate; event ActivatorEventHandler Activated;
/// <summary> /// <summary>
/// Fired when the action center should be made invisible. /// Fired when the action center should be made invisible.
/// </summary> /// </summary>
event ActivatorEventHandler Deactivate; event ActivatorEventHandler Deactivated;
/// <summary> /// <summary>
/// Fired when the action center visibility should be toggled. /// Fired when the action center visibility should be toggled.
/// </summary> /// </summary>
event ActivatorEventHandler Toggle; event ActivatorEventHandler Toggled;
/// <summary> /// <summary>
/// Starts monitoring user input events. /// Starts monitoring user input events.

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2019 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.WindowsApi.Events
{
/// <summary>
/// Event handler fired by the <see cref="ITerminationActivator"/> when the user would like to terminate the application.
/// </summary>
public delegate void TerminationActivatorEventHandler();
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019 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.WindowsApi.Events;
namespace SafeExamBrowser.Contracts.WindowsApi
{
/// <summary>
/// A module which observes user input and indicates when the user would like to terminate the application.
/// </summary>
public interface ITerminationActivator
{
/// <summary>
/// Fired when a termination request has been detected.
/// </summary>
event TerminationActivatorEventHandler Activated;
/// <summary>
/// Temporarily stops processing all user input.
/// </summary>
void Pause();
/// <summary>
/// Resumes processing user input.
/// </summary>
void Resume();
/// <summary>
/// Starts monitoring user input events.
/// </summary>
void Start();
/// <summary>
/// Stops monitoring user input events.
/// </summary>
void Stop();
}
}

View file

@ -82,9 +82,9 @@ namespace SafeExamBrowser.UserInterface.Desktop
public void Register(IActionCenterActivator activator) public void Register(IActionCenterActivator activator)
{ {
activator.Activate += Activator_Activate; activator.Activated += Activator_Activated;
activator.Deactivate += Activator_Deactivate; activator.Deactivated += Activator_Deactivated;
activator.Toggle += Activator_Toggle; activator.Toggled += Activator_Toggled;
} }
public new void Show() public new void Show()
@ -150,7 +150,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
HideAnimated(); HideAnimated();
} }
private void Activator_Activate() private void Activator_Activated()
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {
@ -161,7 +161,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
}); });
} }
private void Activator_Deactivate() private void Activator_Deactivated()
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {
@ -172,7 +172,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
}); });
} }
private void Activator_Toggle() private void Activator_Toggled()
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {

View file

@ -82,9 +82,9 @@ namespace SafeExamBrowser.UserInterface.Mobile
public void Register(IActionCenterActivator activator) public void Register(IActionCenterActivator activator)
{ {
activator.Activate += Activator_Activate; activator.Activated += Activator_Activated;
activator.Deactivate += Activator_Deactivate; activator.Deactivated += Activator_Deactivated;
activator.Toggle += Activator_Toggle; activator.Toggled += Activator_Toggled;
} }
public new void Show() public new void Show()
@ -150,7 +150,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
HideAnimated(); HideAnimated();
} }
private void Activator_Activate() private void Activator_Activated()
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {
@ -161,7 +161,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
}); });
} }
private void Activator_Deactivate() private void Activator_Deactivated()
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {
@ -172,7 +172,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
}); });
} }
private void Activator_Toggle() private void Activator_Toggled()
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {

View file

@ -14,6 +14,7 @@ namespace SafeExamBrowser.WindowsApi.Constants
internal enum VirtualKeyCode internal enum VirtualKeyCode
{ {
A = 0x41, A = 0x41,
Q = 0x51,
Delete = 0x2E, Delete = 0x2E,
LeftAlt = 0xA4, LeftAlt = 0xA4,
LeftControl = 0xA2, LeftControl = 0xA2,

View file

@ -13,7 +13,7 @@ using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types; using SafeExamBrowser.WindowsApi.Types;
namespace SafeExamBrowser.WindowsApi.Monitoring namespace SafeExamBrowser.WindowsApi.Hooks
{ {
internal class KeyboardHook internal class KeyboardHook
{ {

View file

@ -13,7 +13,7 @@ using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types; using SafeExamBrowser.WindowsApi.Types;
namespace SafeExamBrowser.WindowsApi.Monitoring namespace SafeExamBrowser.WindowsApi.Hooks
{ {
internal class MouseHook internal class MouseHook
{ {

View file

@ -12,7 +12,7 @@ using SafeExamBrowser.Contracts.WindowsApi.Events;
using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Delegates;
namespace SafeExamBrowser.WindowsApi.Monitoring namespace SafeExamBrowser.WindowsApi.Hooks
{ {
internal class SystemHook internal class SystemHook
{ {

View file

@ -25,9 +25,9 @@ namespace SafeExamBrowser.WindowsApi
private HookDelegate hookDelegate; private HookDelegate hookDelegate;
private ILogger logger; private ILogger logger;
public event ActivatorEventHandler Activate { add { } remove { } } public event ActivatorEventHandler Activated { add { } remove { } }
public event ActivatorEventHandler Deactivate { add { } remove { } } public event ActivatorEventHandler Deactivated { add { } remove { } }
public event ActivatorEventHandler Toggle; public event ActivatorEventHandler Toggled;
public KeyboardActivator(ILogger logger) public KeyboardActivator(ILogger logger)
{ {
@ -93,7 +93,7 @@ namespace SafeExamBrowser.WindowsApi
if (A && LeftWindows && changed) if (A && LeftWindows && changed)
{ {
logger.Debug("Detected toggle sequence for action center."); logger.Debug("Detected toggle sequence for action center.");
Toggle?.Invoke(); Toggled?.Invoke();
return (IntPtr) 1; return (IntPtr) 1;
} }

View file

@ -18,7 +18,7 @@ using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.WindowsApi; using SafeExamBrowser.Contracts.WindowsApi;
using SafeExamBrowser.Contracts.WindowsApi.Events; using SafeExamBrowser.Contracts.WindowsApi.Events;
using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Monitoring; using SafeExamBrowser.WindowsApi.Hooks;
using SafeExamBrowser.WindowsApi.Types; using SafeExamBrowser.WindowsApi.Types;
namespace SafeExamBrowser.WindowsApi namespace SafeExamBrowser.WindowsApi

View file

@ -64,8 +64,9 @@
<Compile Include="DesktopFactory.cs" /> <Compile Include="DesktopFactory.cs" />
<Compile Include="ExplorerShell.cs" /> <Compile Include="ExplorerShell.cs" />
<Compile Include="KeyboardActivator.cs" /> <Compile Include="KeyboardActivator.cs" />
<Compile Include="Monitoring\MouseHook.cs" /> <Compile Include="Hooks\MouseHook.cs" />
<Compile Include="Monitoring\SystemHook.cs" /> <Compile Include="Hooks\SystemHook.cs" />
<Compile Include="TerminationActivator.cs" />
<Compile Include="Process.cs" /> <Compile Include="Process.cs" />
<Compile Include="ProcessFactory.cs" /> <Compile Include="ProcessFactory.cs" />
<Compile Include="Constants\AccessMask.cs" /> <Compile Include="Constants\AccessMask.cs" />
@ -77,7 +78,7 @@
<Compile Include="Constants\ShowWindowCommand.cs" /> <Compile Include="Constants\ShowWindowCommand.cs" />
<Compile Include="Constants\SystemCommand.cs" /> <Compile Include="Constants\SystemCommand.cs" />
<Compile Include="Kernel32.cs" /> <Compile Include="Kernel32.cs" />
<Compile Include="Monitoring\KeyboardHook.cs" /> <Compile Include="Hooks\KeyboardHook.cs" />
<Compile Include="NativeMethods.cs" /> <Compile Include="NativeMethods.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Constants\SPI.cs" /> <Compile Include="Constants\SPI.cs" />

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2019 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.Runtime.InteropServices;
using System.Threading;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.WindowsApi;
using SafeExamBrowser.Contracts.WindowsApi.Events;
using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types;
namespace SafeExamBrowser.WindowsApi
{
public class TerminationActivator : ITerminationActivator
{
private bool Q, LeftCtrl, RightCtrl, paused;
private IntPtr handle;
private HookDelegate hookDelegate;
private ILogger logger;
public event TerminationActivatorEventHandler Activated;
public TerminationActivator(ILogger logger)
{
this.logger = logger;
}
public void Pause()
{
paused = true;
}
public void Resume()
{
Q = false;
LeftCtrl = false;
RightCtrl = false;
paused = false;
}
public void Start()
{
var hookReadyEvent = new AutoResetEvent(false);
var hookThread = new Thread(() =>
{
var sleepEvent = new AutoResetEvent(false);
var process = System.Diagnostics.Process.GetCurrentProcess();
var module = process.MainModule;
var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName);
// IMPORTANT:
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelKeyboardProc);
handle = User32.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hookDelegate, moduleHandle, 0);
hookReadyEvent.Set();
while (true)
{
sleepEvent.WaitOne();
}
});
hookThread.SetApartmentState(ApartmentState.STA);
hookThread.IsBackground = true;
hookThread.Start();
hookReadyEvent.WaitOne();
}
public void Stop()
{
User32.UnhookWindowsHookEx(handle);
}
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && !paused)
{
var changed = false;
var keyData = (KBDLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
var pressed = IsPressed(wParam.ToInt32());
switch (keyData.KeyCode)
{
case (uint) VirtualKeyCode.Q:
changed = Q != pressed;
Q = pressed;
break;
case (uint) VirtualKeyCode.LeftControl:
changed = LeftCtrl != pressed;
LeftCtrl = pressed;
break;
case (uint) VirtualKeyCode.RightControl:
changed = RightCtrl != pressed;
RightCtrl = pressed;
break;
}
if (Q && (LeftCtrl || RightCtrl) && changed)
{
logger.Debug("Detected termination sequence.");
Activated?.Invoke();
return (IntPtr) 1;
}
}
return User32.CallNextHookEx(handle, nCode, wParam, lParam);
}
private bool IsPressed(int wParam)
{
return wParam == Constant.WM_KEYDOWN || wParam == Constant.WM_SYSKEYDOWN;
}
}
}

View file

@ -26,9 +26,9 @@ namespace SafeExamBrowser.WindowsApi
private bool isDown; private bool isDown;
private ILogger logger; private ILogger logger;
public event ActivatorEventHandler Activate; public event ActivatorEventHandler Activated;
public event ActivatorEventHandler Deactivate { add { } remove { } } public event ActivatorEventHandler Deactivated { add { } remove { } }
public event ActivatorEventHandler Toggle { add { } remove { } } public event ActivatorEventHandler Toggled { add { } remove { } }
public TouchActivator(ILogger logger) public TouchActivator(ILogger logger)
{ {
@ -109,7 +109,7 @@ namespace SafeExamBrowser.WindowsApi
if (isDown && hasMoved) if (isDown && hasMoved)
{ {
logger.Debug("Detected activation gesture for action center."); logger.Debug("Detected activation gesture for action center.");
Activate?.Invoke(); Activated?.Invoke();
} }
} }