diff --git a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs index df732729..c2b3be65 100644 --- a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs +++ b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs @@ -49,6 +49,7 @@ namespace SafeExamBrowser.Client.UnitTests private Settings settings; private Mock shutdown; private Mock taskbar; + private Mock terminationActivator; private Mock text; private Mock uiFactory; private Mock windowMonitor; @@ -74,6 +75,7 @@ namespace SafeExamBrowser.Client.UnitTests settings = new Settings(); shutdown = new Mock(); taskbar = new Mock(); + terminationActivator = new Mock(); text = new Mock(); uiFactory = new Mock(); windowMonitor = new Mock(); @@ -94,6 +96,7 @@ namespace SafeExamBrowser.Client.UnitTests runtimeProxy.Object, shutdown.Object, taskbar.Object, + terminationActivator.Object, text.Object, uiFactory.Object, windowMonitor.Object); diff --git a/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs index 21f73c01..c4eab709 100644 --- a/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs +++ b/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs @@ -17,6 +17,7 @@ using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface.Shell; +using SafeExamBrowser.Contracts.WindowsApi; namespace SafeExamBrowser.Client.UnitTests.Operations { @@ -28,6 +29,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations private ActionCenterSettings actionCenterSettings; private Mock logger; private TaskbarSettings taskbarSettings; + private Mock terminationActivator; private Mock aboutInfo; private Mock aboutController; private Mock logInfo; @@ -59,6 +61,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations systemInfo = new Mock(); taskbar = new Mock(); taskbarSettings = new TaskbarSettings(); + terminationActivator = new Mock(); text = new Mock(); uiFactory = new Mock(); @@ -84,6 +87,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations systemInfo.Object, taskbar.Object, taskbarSettings, + terminationActivator.Object, text.Object, uiFactory.Object); } diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs index 1322556b..288d5957 100644 --- a/SafeExamBrowser.Client/ClientController.cs +++ b/SafeExamBrowser.Client/ClientController.cs @@ -44,6 +44,7 @@ namespace SafeExamBrowser.Client private Action shutdown; private ISplashScreen splashScreen; private ITaskbar taskbar; + private ITerminationActivator terminationActivator; private IText text; private IUserInterfaceFactory uiFactory; private IWindowMonitor windowMonitor; @@ -79,6 +80,7 @@ namespace SafeExamBrowser.Client IRuntimeProxy runtime, Action shutdown, ITaskbar taskbar, + ITerminationActivator terminationActivator, IText text, IUserInterfaceFactory uiFactory, IWindowMonitor windowMonitor) @@ -94,6 +96,7 @@ namespace SafeExamBrowser.Client this.runtime = runtime; this.shutdown = shutdown; this.taskbar = taskbar; + this.terminationActivator = terminationActivator; this.text = text; this.uiFactory = uiFactory; this.windowMonitor = windowMonitor; @@ -178,6 +181,7 @@ namespace SafeExamBrowser.Client processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted; runtime.ConnectionLost += Runtime_ConnectionLost; taskbar.QuitButtonClicked += Shell_QuitButtonClicked; + terminationActivator.Activated += TerminationActivator_Activated; windowMonitor.WindowChanged += WindowMonitor_WindowChanged; } @@ -188,6 +192,7 @@ namespace SafeExamBrowser.Client processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted; runtime.ConnectionLost -= Runtime_ConnectionLost; taskbar.QuitButtonClicked -= Shell_QuitButtonClicked; + terminationActivator.Activated -= TerminationActivator_Activated; windowMonitor.WindowChanged -= WindowMonitor_WindowChanged; if (Browser != null) @@ -383,6 +388,35 @@ namespace SafeExamBrowser.Client } 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 requestShutdown = false; @@ -400,31 +434,18 @@ namespace SafeExamBrowser.Client { var communication = runtime.RequestShutdown(); - if (!communication.Success) + if (communication.Success) + { + return true; + } + else { logger.Error("Failed to communicate shutdown request to the runtime!"); messageBox.Show(TextKey.MessageBox_QuitError, TextKey.MessageBox_QuitErrorTitle, icon: MessageBoxIcon.Error); } } - else - { - args.Cancel = true; - } - } - private void WindowMonitor_WindowChanged(IntPtr window) - { - var allowed = processMonitor.BelongsToAllowedProcess(window); - - if (!allowed) - { - var success = windowMonitor.Hide(window); - - if (!success) - { - windowMonitor.Close(window); - } - } + return false; } private bool TryConfirmShutdown() diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index f5760e95..68b94b36 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -71,6 +71,7 @@ namespace SafeExamBrowser.Client private ISystemComponent wirelessNetwork; private ISystemInfo systemInfo; private ITaskbar taskbar; + private ITerminationActivator terminationActivator; private IText text; private ITextResource textResource; private IUserInterfaceFactory uiFactory; @@ -98,6 +99,7 @@ namespace SafeExamBrowser.Client uiFactory = BuildUserInterfaceFactory(); runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy))); taskbar = BuildTaskbar(); + terminationActivator = new TerminationActivator(new ModuleLogger(logger, nameof(TerminationActivator))); windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods); wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, nameof(WirelessNetwork)), text); @@ -137,6 +139,7 @@ namespace SafeExamBrowser.Client runtimeProxy, shutdown, taskbar, + terminationActivator, text, uiFactory, windowMonitor); @@ -279,6 +282,7 @@ namespace SafeExamBrowser.Client systemInfo, taskbar, configuration.Settings.Taskbar, + terminationActivator, text, uiFactory); diff --git a/SafeExamBrowser.Client/Operations/ShellOperation.cs b/SafeExamBrowser.Client/Operations/ShellOperation.cs index ec10f2c9..8fa11e35 100644 --- a/SafeExamBrowser.Client/Operations/ShellOperation.cs +++ b/SafeExamBrowser.Client/Operations/ShellOperation.cs @@ -16,6 +16,7 @@ using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface.Shell; +using SafeExamBrowser.Contracts.WindowsApi; namespace SafeExamBrowser.Client.Operations { @@ -35,6 +36,7 @@ namespace SafeExamBrowser.Client.Operations private ISystemInfo systemInfo; private ITaskbar taskbar; private TaskbarSettings taskbarSettings; + private ITerminationActivator terminationActivator; private IText text; private IUserInterfaceFactory uiFactory; @@ -56,6 +58,7 @@ namespace SafeExamBrowser.Client.Operations ISystemInfo systemInfo, ITaskbar taskbar, TaskbarSettings taskbarSettings, + ITerminationActivator terminationActivator, IText text, IUserInterfaceFactory uiFactory) { @@ -71,6 +74,7 @@ namespace SafeExamBrowser.Client.Operations this.powerSupply = powerSupply; this.systemInfo = systemInfo; this.taskbarSettings = taskbarSettings; + this.terminationActivator = terminationActivator; this.text = text; this.taskbar = taskbar; this.uiFactory = uiFactory; @@ -85,6 +89,7 @@ namespace SafeExamBrowser.Client.Operations InitializeSystemComponents(); InitializeActionCenter(); InitializeTaskbar(); + InitializeActivators(); return OperationResult.Success; } @@ -101,6 +106,11 @@ namespace SafeExamBrowser.Client.Operations return OperationResult.Success; } + private void InitializeActivators() + { + terminationActivator.Start(); + } + private void InitializeActionCenter() { if (actionCenterSettings.EnableActionCenter) @@ -270,6 +280,8 @@ namespace SafeExamBrowser.Client.Operations private void TerminateActivators() { + terminationActivator.Stop(); + if (actionCenterSettings.EnableActionCenter) { foreach (var activator in activators) diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 4cecb85b..b3dd3874 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -218,6 +218,7 @@ + @@ -225,6 +226,7 @@ + diff --git a/SafeExamBrowser.Contracts/UserInterface/Shell/IActionCenterActivator.cs b/SafeExamBrowser.Contracts/UserInterface/Shell/IActionCenterActivator.cs index e59153c6..ac59081d 100644 --- a/SafeExamBrowser.Contracts/UserInterface/Shell/IActionCenterActivator.cs +++ b/SafeExamBrowser.Contracts/UserInterface/Shell/IActionCenterActivator.cs @@ -18,17 +18,17 @@ namespace SafeExamBrowser.Contracts.UserInterface.Shell /// /// Fired when the action center should be made visible. /// - event ActivatorEventHandler Activate; + event ActivatorEventHandler Activated; /// /// Fired when the action center should be made invisible. /// - event ActivatorEventHandler Deactivate; + event ActivatorEventHandler Deactivated; /// /// Fired when the action center visibility should be toggled. /// - event ActivatorEventHandler Toggle; + event ActivatorEventHandler Toggled; /// /// Starts monitoring user input events. diff --git a/SafeExamBrowser.Contracts/WindowsApi/Events/TerminationActivatorEventHandler.cs b/SafeExamBrowser.Contracts/WindowsApi/Events/TerminationActivatorEventHandler.cs new file mode 100644 index 00000000..d49e4f7f --- /dev/null +++ b/SafeExamBrowser.Contracts/WindowsApi/Events/TerminationActivatorEventHandler.cs @@ -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 +{ + /// + /// Event handler fired by the when the user would like to terminate the application. + /// + public delegate void TerminationActivatorEventHandler(); +} diff --git a/SafeExamBrowser.Contracts/WindowsApi/ITerminationActivator.cs b/SafeExamBrowser.Contracts/WindowsApi/ITerminationActivator.cs new file mode 100644 index 00000000..488f6936 --- /dev/null +++ b/SafeExamBrowser.Contracts/WindowsApi/ITerminationActivator.cs @@ -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 +{ + /// + /// A module which observes user input and indicates when the user would like to terminate the application. + /// + public interface ITerminationActivator + { + /// + /// Fired when a termination request has been detected. + /// + event TerminationActivatorEventHandler Activated; + + /// + /// Temporarily stops processing all user input. + /// + void Pause(); + + /// + /// Resumes processing user input. + /// + void Resume(); + + /// + /// Starts monitoring user input events. + /// + void Start(); + + /// + /// Stops monitoring user input events. + /// + void Stop(); + } +} diff --git a/SafeExamBrowser.UserInterface.Desktop/ActionCenter.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/ActionCenter.xaml.cs index 8bd13c35..9f0fb949 100644 --- a/SafeExamBrowser.UserInterface.Desktop/ActionCenter.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/ActionCenter.xaml.cs @@ -82,9 +82,9 @@ namespace SafeExamBrowser.UserInterface.Desktop public void Register(IActionCenterActivator activator) { - activator.Activate += Activator_Activate; - activator.Deactivate += Activator_Deactivate; - activator.Toggle += Activator_Toggle; + activator.Activated += Activator_Activated; + activator.Deactivated += Activator_Deactivated; + activator.Toggled += Activator_Toggled; } public new void Show() @@ -150,7 +150,7 @@ namespace SafeExamBrowser.UserInterface.Desktop HideAnimated(); } - private void Activator_Activate() + private void Activator_Activated() { Dispatcher.InvokeAsync(() => { @@ -161,7 +161,7 @@ namespace SafeExamBrowser.UserInterface.Desktop }); } - private void Activator_Deactivate() + private void Activator_Deactivated() { Dispatcher.InvokeAsync(() => { @@ -172,7 +172,7 @@ namespace SafeExamBrowser.UserInterface.Desktop }); } - private void Activator_Toggle() + private void Activator_Toggled() { Dispatcher.InvokeAsync(() => { diff --git a/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml.cs index df54e78a..43007b9d 100644 --- a/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml.cs @@ -82,9 +82,9 @@ namespace SafeExamBrowser.UserInterface.Mobile public void Register(IActionCenterActivator activator) { - activator.Activate += Activator_Activate; - activator.Deactivate += Activator_Deactivate; - activator.Toggle += Activator_Toggle; + activator.Activated += Activator_Activated; + activator.Deactivated += Activator_Deactivated; + activator.Toggled += Activator_Toggled; } public new void Show() @@ -150,7 +150,7 @@ namespace SafeExamBrowser.UserInterface.Mobile HideAnimated(); } - private void Activator_Activate() + private void Activator_Activated() { Dispatcher.InvokeAsync(() => { @@ -161,7 +161,7 @@ namespace SafeExamBrowser.UserInterface.Mobile }); } - private void Activator_Deactivate() + private void Activator_Deactivated() { Dispatcher.InvokeAsync(() => { @@ -172,7 +172,7 @@ namespace SafeExamBrowser.UserInterface.Mobile }); } - private void Activator_Toggle() + private void Activator_Toggled() { Dispatcher.InvokeAsync(() => { diff --git a/SafeExamBrowser.WindowsApi/Constants/VirtualKeyCode.cs b/SafeExamBrowser.WindowsApi/Constants/VirtualKeyCode.cs index 14adb28d..8a1fc29d 100644 --- a/SafeExamBrowser.WindowsApi/Constants/VirtualKeyCode.cs +++ b/SafeExamBrowser.WindowsApi/Constants/VirtualKeyCode.cs @@ -14,6 +14,7 @@ namespace SafeExamBrowser.WindowsApi.Constants internal enum VirtualKeyCode { A = 0x41, + Q = 0x51, Delete = 0x2E, LeftAlt = 0xA4, LeftControl = 0xA2, diff --git a/SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs b/SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs similarity index 98% rename from SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs rename to SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs index 40c34a69..77335ab8 100644 --- a/SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs +++ b/SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs @@ -13,7 +13,7 @@ using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Types; -namespace SafeExamBrowser.WindowsApi.Monitoring +namespace SafeExamBrowser.WindowsApi.Hooks { internal class KeyboardHook { diff --git a/SafeExamBrowser.WindowsApi/Monitoring/MouseHook.cs b/SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs similarity index 98% rename from SafeExamBrowser.WindowsApi/Monitoring/MouseHook.cs rename to SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs index f6f459e1..2ad1080d 100644 --- a/SafeExamBrowser.WindowsApi/Monitoring/MouseHook.cs +++ b/SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs @@ -13,7 +13,7 @@ using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Types; -namespace SafeExamBrowser.WindowsApi.Monitoring +namespace SafeExamBrowser.WindowsApi.Hooks { internal class MouseHook { diff --git a/SafeExamBrowser.WindowsApi/Monitoring/SystemHook.cs b/SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs similarity index 97% rename from SafeExamBrowser.WindowsApi/Monitoring/SystemHook.cs rename to SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs index 26460a47..ef26d893 100644 --- a/SafeExamBrowser.WindowsApi/Monitoring/SystemHook.cs +++ b/SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs @@ -12,7 +12,7 @@ using SafeExamBrowser.Contracts.WindowsApi.Events; using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Delegates; -namespace SafeExamBrowser.WindowsApi.Monitoring +namespace SafeExamBrowser.WindowsApi.Hooks { internal class SystemHook { diff --git a/SafeExamBrowser.WindowsApi/KeyboardActivator.cs b/SafeExamBrowser.WindowsApi/KeyboardActivator.cs index afbd748c..d8d1b15d 100644 --- a/SafeExamBrowser.WindowsApi/KeyboardActivator.cs +++ b/SafeExamBrowser.WindowsApi/KeyboardActivator.cs @@ -25,9 +25,9 @@ namespace SafeExamBrowser.WindowsApi private HookDelegate hookDelegate; private ILogger logger; - public event ActivatorEventHandler Activate { add { } remove { } } - public event ActivatorEventHandler Deactivate { add { } remove { } } - public event ActivatorEventHandler Toggle; + public event ActivatorEventHandler Activated { add { } remove { } } + public event ActivatorEventHandler Deactivated { add { } remove { } } + public event ActivatorEventHandler Toggled; public KeyboardActivator(ILogger logger) { @@ -93,7 +93,7 @@ namespace SafeExamBrowser.WindowsApi if (A && LeftWindows && changed) { logger.Debug("Detected toggle sequence for action center."); - Toggle?.Invoke(); + Toggled?.Invoke(); return (IntPtr) 1; } diff --git a/SafeExamBrowser.WindowsApi/NativeMethods.cs b/SafeExamBrowser.WindowsApi/NativeMethods.cs index eacdb8ac..dfbbd85f 100644 --- a/SafeExamBrowser.WindowsApi/NativeMethods.cs +++ b/SafeExamBrowser.WindowsApi/NativeMethods.cs @@ -18,7 +18,7 @@ using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.WindowsApi; using SafeExamBrowser.Contracts.WindowsApi.Events; using SafeExamBrowser.WindowsApi.Constants; -using SafeExamBrowser.WindowsApi.Monitoring; +using SafeExamBrowser.WindowsApi.Hooks; using SafeExamBrowser.WindowsApi.Types; namespace SafeExamBrowser.WindowsApi diff --git a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj index d848fc84..affd47c7 100644 --- a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj +++ b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj @@ -64,8 +64,9 @@ - - + + + @@ -77,7 +78,7 @@ - + diff --git a/SafeExamBrowser.WindowsApi/TerminationActivator.cs b/SafeExamBrowser.WindowsApi/TerminationActivator.cs new file mode 100644 index 00000000..60347423 --- /dev/null +++ b/SafeExamBrowser.WindowsApi/TerminationActivator.cs @@ -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 CallbackOnCollectedDelegate 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; + } + } +} diff --git a/SafeExamBrowser.WindowsApi/TouchActivator.cs b/SafeExamBrowser.WindowsApi/TouchActivator.cs index 83671305..8ec2c7d1 100644 --- a/SafeExamBrowser.WindowsApi/TouchActivator.cs +++ b/SafeExamBrowser.WindowsApi/TouchActivator.cs @@ -26,9 +26,9 @@ namespace SafeExamBrowser.WindowsApi private bool isDown; private ILogger logger; - public event ActivatorEventHandler Activate; - public event ActivatorEventHandler Deactivate { add { } remove { } } - public event ActivatorEventHandler Toggle { add { } remove { } } + public event ActivatorEventHandler Activated; + public event ActivatorEventHandler Deactivated { add { } remove { } } + public event ActivatorEventHandler Toggled { add { } remove { } } public TouchActivator(ILogger logger) { @@ -109,7 +109,7 @@ namespace SafeExamBrowser.WindowsApi if (isDown && hasMoved) { logger.Debug("Detected activation gesture for action center."); - Activate?.Invoke(); + Activated?.Invoke(); } }