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 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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
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(() =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -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(() =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
|
@ -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
|
||||||
{
|
{
|
|
@ -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
|
||||||
{
|
{
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
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 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue