SEBWIN-226: Implemented termination activator for global CTRL+Q keyboard shortcut.
This commit is contained in:
parent
25022295e1
commit
719411d8e3
20 changed files with 280 additions and 49 deletions
|
@ -49,6 +49,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
private Settings settings;
|
||||
private Mock<Action> shutdown;
|
||||
private Mock<ITaskbar> taskbar;
|
||||
private Mock<ITerminationActivator> terminationActivator;
|
||||
private Mock<IText> text;
|
||||
private Mock<IUserInterfaceFactory> uiFactory;
|
||||
private Mock<IWindowMonitor> windowMonitor;
|
||||
|
@ -74,6 +75,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
settings = new Settings();
|
||||
shutdown = new Mock<Action>();
|
||||
taskbar = new Mock<ITaskbar>();
|
||||
terminationActivator = new Mock<ITerminationActivator>();
|
||||
text = new Mock<IText>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
windowMonitor = new Mock<IWindowMonitor>();
|
||||
|
@ -94,6 +96,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
runtimeProxy.Object,
|
||||
shutdown.Object,
|
||||
taskbar.Object,
|
||||
terminationActivator.Object,
|
||||
text.Object,
|
||||
uiFactory.Object,
|
||||
windowMonitor.Object);
|
||||
|
|
|
@ -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<ILogger> logger;
|
||||
private TaskbarSettings taskbarSettings;
|
||||
private Mock<ITerminationActivator> terminationActivator;
|
||||
private Mock<INotificationInfo> aboutInfo;
|
||||
private Mock<INotificationController> aboutController;
|
||||
private Mock<INotificationInfo> logInfo;
|
||||
|
@ -59,6 +61,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
systemInfo = new Mock<ISystemInfo>();
|
||||
taskbar = new Mock<ITaskbar>();
|
||||
taskbarSettings = new TaskbarSettings();
|
||||
terminationActivator = new Mock<ITerminationActivator>();
|
||||
text = new Mock<IText>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
|
||||
|
@ -84,6 +87,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
systemInfo.Object,
|
||||
taskbar.Object,
|
||||
taskbarSettings,
|
||||
terminationActivator.Object,
|
||||
text.Object,
|
||||
uiFactory.Object);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -71,6 +71,7 @@ namespace SafeExamBrowser.Client
|
|||
private ISystemComponent<ISystemWirelessNetworkControl> 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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -218,6 +218,7 @@
|
|||
<Compile Include="UserInterface\MessageBox\MessageBoxIcon.cs" />
|
||||
<Compile Include="WindowsApi\Events\ProcessTerminatedEventHandler.cs" />
|
||||
<Compile Include="WindowsApi\Events\SystemEventCallback.cs" />
|
||||
<Compile Include="WindowsApi\Events\TerminationActivatorEventHandler.cs" />
|
||||
<Compile Include="WindowsApi\IBounds.cs" />
|
||||
<Compile Include="WindowsApi\IDesktop.cs" />
|
||||
<Compile Include="WindowsApi\IDesktopFactory.cs" />
|
||||
|
@ -225,6 +226,7 @@
|
|||
<Compile Include="WindowsApi\INativeMethods.cs" />
|
||||
<Compile Include="WindowsApi\IProcess.cs" />
|
||||
<Compile Include="WindowsApi\IProcessFactory.cs" />
|
||||
<Compile Include="WindowsApi\ITerminationActivator.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
|
|
@ -18,17 +18,17 @@ namespace SafeExamBrowser.Contracts.UserInterface.Shell
|
|||
/// <summary>
|
||||
/// Fired when the action center should be made visible.
|
||||
/// </summary>
|
||||
event ActivatorEventHandler Activate;
|
||||
event ActivatorEventHandler Activated;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the action center should be made invisible.
|
||||
/// </summary>
|
||||
event ActivatorEventHandler Deactivate;
|
||||
event ActivatorEventHandler Deactivated;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the action center visibility should be toggled.
|
||||
/// </summary>
|
||||
event ActivatorEventHandler Toggle;
|
||||
event ActivatorEventHandler Toggled;
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring user input events.
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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(() =>
|
||||
{
|
||||
|
|
|
@ -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(() =>
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace SafeExamBrowser.WindowsApi.Constants
|
|||
internal enum VirtualKeyCode
|
||||
{
|
||||
A = 0x41,
|
||||
Q = 0x51,
|
||||
Delete = 0x2E,
|
||||
LeftAlt = 0xA4,
|
||||
LeftControl = 0xA2,
|
||||
|
|
|
@ -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
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -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
|
||||
{
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -64,8 +64,9 @@
|
|||
<Compile Include="DesktopFactory.cs" />
|
||||
<Compile Include="ExplorerShell.cs" />
|
||||
<Compile Include="KeyboardActivator.cs" />
|
||||
<Compile Include="Monitoring\MouseHook.cs" />
|
||||
<Compile Include="Monitoring\SystemHook.cs" />
|
||||
<Compile Include="Hooks\MouseHook.cs" />
|
||||
<Compile Include="Hooks\SystemHook.cs" />
|
||||
<Compile Include="TerminationActivator.cs" />
|
||||
<Compile Include="Process.cs" />
|
||||
<Compile Include="ProcessFactory.cs" />
|
||||
<Compile Include="Constants\AccessMask.cs" />
|
||||
|
@ -77,7 +78,7 @@
|
|||
<Compile Include="Constants\ShowWindowCommand.cs" />
|
||||
<Compile Include="Constants\SystemCommand.cs" />
|
||||
<Compile Include="Kernel32.cs" />
|
||||
<Compile Include="Monitoring\KeyboardHook.cs" />
|
||||
<Compile Include="Hooks\KeyboardHook.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Constants\SPI.cs" />
|
||||
|
|
125
SafeExamBrowser.WindowsApi/TerminationActivator.cs
Normal file
125
SafeExamBrowser.WindowsApi/TerminationActivator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue