Renamed Key to TextKey and started implementing keyboard monitoring.
This commit is contained in:
parent
cbb3e797b0
commit
c9a53397a4
33 changed files with 462 additions and 47 deletions
|
@ -34,7 +34,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
|
||||
if (settings.AllowDeveloperConsole)
|
||||
{
|
||||
model.AddItem((CefMenuCommand) DEV_CONSOLE_COMMAND, text.Get(Key.Browser_ShowDeveloperConsole));
|
||||
model.AddItem((CefMenuCommand) DEV_CONSOLE_COMMAND, text.Get(TextKey.Browser_ShowDeveloperConsole));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,6 @@ namespace SafeExamBrowser.Contracts.I18n
|
|||
/// Gets the text associated with the specified key. If the key was not found, a default text indicating
|
||||
/// that the given key is not configured shall be returned.
|
||||
/// </summary>
|
||||
string Get(Key key);
|
||||
string Get(TextKey key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,6 @@ namespace SafeExamBrowser.Contracts.I18n
|
|||
/// <summary>
|
||||
/// Loads all text data from a resource.
|
||||
/// </summary>
|
||||
IDictionary<Key, string> LoadText();
|
||||
IDictionary<TextKey, string> LoadText();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace SafeExamBrowser.Contracts.I18n
|
|||
/// <summary>
|
||||
/// Defines all text components of the user interface.
|
||||
/// </summary>
|
||||
public enum Key
|
||||
public enum TextKey
|
||||
{
|
||||
Browser_ShowDeveloperConsole,
|
||||
MessageBox_ShutdownError,
|
|
@ -10,5 +10,9 @@ namespace SafeExamBrowser.Contracts.Monitoring
|
|||
{
|
||||
public interface IKeyboardInterceptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if the given key should be blocked, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool Block(uint keyCode, KeyModifier modifier);
|
||||
}
|
||||
}
|
||||
|
|
20
SafeExamBrowser.Contracts/Monitoring/KeyModifier.cs
Normal file
20
SafeExamBrowser.Contracts/Monitoring/KeyModifier.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2017 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;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Monitoring
|
||||
{
|
||||
[Flags]
|
||||
public enum KeyModifier
|
||||
{
|
||||
None = 0,
|
||||
Ctrl = 0b01,
|
||||
Alt = 0b10
|
||||
}
|
||||
}
|
|
@ -72,7 +72,7 @@
|
|||
<Compile Include="Behaviour\IStartupController.cs" />
|
||||
<Compile Include="Configuration\IWorkingArea.cs" />
|
||||
<Compile Include="I18n\IText.cs" />
|
||||
<Compile Include="I18n\Key.cs" />
|
||||
<Compile Include="I18n\TextKey.cs" />
|
||||
<Compile Include="Logging\ILogContent.cs" />
|
||||
<Compile Include="Logging\ILogger.cs" />
|
||||
<Compile Include="Logging\ILogMessage.cs" />
|
||||
|
@ -84,6 +84,7 @@
|
|||
<Compile Include="Monitoring\IMouseInterceptor.cs" />
|
||||
<Compile Include="Monitoring\IProcessMonitor.cs" />
|
||||
<Compile Include="Monitoring\IWindowMonitor.cs" />
|
||||
<Compile Include="Monitoring\KeyModifier.cs" />
|
||||
<Compile Include="UserInterface\IBrowserControl.cs" />
|
||||
<Compile Include="UserInterface\IBrowserWindow.cs" />
|
||||
<Compile Include="UserInterface\IMessageBox.cs" />
|
||||
|
|
|
@ -47,6 +47,6 @@ namespace SafeExamBrowser.Contracts.UserInterface
|
|||
/// Updates the status text of the splash screen. If the busy flag is set,
|
||||
/// the splash screen will show an animation to indicate a long-running operation.
|
||||
/// </summary>
|
||||
void UpdateText(Key key, bool showBusyIndication = false);
|
||||
void UpdateText(TextKey key, bool showBusyIndication = false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
using SafeExamBrowser.Contracts.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.WindowsApi
|
||||
|
@ -71,6 +72,11 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
|||
/// </exception>
|
||||
void PostCloseMessageToShell();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a system hook for the given keyboard interceptor.
|
||||
/// </summary>
|
||||
void RegisterKeyboardHook(IKeyboardInterceptor interceptor);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a system event which will invoke the specified callback when the foreground window has changed.
|
||||
/// Returns a handle to the newly registered Windows event hook.
|
||||
|
@ -101,6 +107,14 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
|||
/// </exception>
|
||||
void SetWorkingArea(RECT bounds);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the system hook for the given keyboard interceptor.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">
|
||||
/// If the hook for the given interceptor could not be successfully removed.
|
||||
/// </exception>
|
||||
void UnregisterKeyboardHook(IKeyboardInterceptor interceptor);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a previously registered system event.
|
||||
/// </summary>
|
||||
|
|
|
@ -24,9 +24,9 @@ namespace SafeExamBrowser.Core.UnitTests.I18n
|
|||
var resource = new Mock<ITextResource>();
|
||||
var sut = new Text(resource.Object);
|
||||
|
||||
resource.Setup(r => r.LoadText()).Returns<IDictionary<Key, string>>(null);
|
||||
resource.Setup(r => r.LoadText()).Returns<IDictionary<TextKey, string>>(null);
|
||||
|
||||
var text = sut.Get((Key)(-1));
|
||||
var text = sut.Get((TextKey)(-1));
|
||||
|
||||
Assert.IsNotNull(text);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Perform()
|
||||
{
|
||||
logger.Info("Initializing browser...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_InitializeBrowser);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeBrowser);
|
||||
|
||||
var browserButton = uiFactory.CreateApplicationButton(browserInfo);
|
||||
|
||||
|
@ -54,7 +54,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Revert()
|
||||
{
|
||||
logger.Info("Terminating browser...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_TerminateBrowser);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_TerminateBrowser);
|
||||
|
||||
browserController.Terminate();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Perform()
|
||||
{
|
||||
logger.Info("Starting event handling...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_StartEventHandling);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_StartEventHandling);
|
||||
|
||||
controller.Start();
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Revert()
|
||||
{
|
||||
logger.Info("Stopping event handling...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_StopEventHandling);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_StopEventHandling);
|
||||
|
||||
controller.Stop();
|
||||
}
|
||||
|
|
|
@ -30,12 +30,12 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Perform()
|
||||
{
|
||||
logger.Info("Initializing process monitoring...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_WaitExplorerTermination, true);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_WaitExplorerTermination, true);
|
||||
|
||||
processMonitor.CloseExplorerShell();
|
||||
processMonitor.StartMonitoringExplorer();
|
||||
|
||||
SplashScreen.UpdateText(Key.SplashScreen_InitializeProcessMonitoring);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeProcessMonitoring);
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
@ -43,11 +43,11 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Revert()
|
||||
{
|
||||
logger.Info("Stopping process monitoring...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_StopProcessMonitoring);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_StopProcessMonitoring);
|
||||
|
||||
// TODO
|
||||
|
||||
SplashScreen.UpdateText(Key.SplashScreen_WaitExplorerStartup, true);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_WaitExplorerStartup, true);
|
||||
|
||||
processMonitor.StopMonitoringExplorer();
|
||||
processMonitor.StartExplorerShell();
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Perform()
|
||||
{
|
||||
logger.Info("Initializing taskbar...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_InitializeTaskbar);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeTaskbar);
|
||||
|
||||
var aboutInfo = new AboutNotificationInfo(text);
|
||||
var aboutNotification = uiFactory.CreateNotification(aboutInfo);
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Perform()
|
||||
{
|
||||
logger.Info("Initializing window monitoring...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_InitializeWindowMonitoring);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeWindowMonitoring);
|
||||
|
||||
windowMonitor.HideAllWindows();
|
||||
windowMonitor.StartMonitoringWindows();
|
||||
|
@ -39,7 +39,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Revert()
|
||||
{
|
||||
logger.Info("Stopping window monitoring...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_StopWindowMonitoring);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_StopWindowMonitoring);
|
||||
|
||||
windowMonitor.StopMonitoringWindows();
|
||||
windowMonitor.RestoreHiddenWindows();
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Perform()
|
||||
{
|
||||
logger.Info("Initializing working area...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_InitializeWorkingArea);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeWorkingArea);
|
||||
|
||||
// TODO
|
||||
// - Emptying clipboard
|
||||
|
@ -43,7 +43,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
public void Revert()
|
||||
{
|
||||
logger.Info("Restoring working area...");
|
||||
SplashScreen.UpdateText(Key.SplashScreen_RestoreWorkingArea);
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_RestoreWorkingArea);
|
||||
|
||||
// TODO
|
||||
// - Emptying clipboard
|
||||
|
|
|
@ -63,14 +63,14 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
|
||||
splashScreen = uiFactory.CreateSplashScreen(settings, text);
|
||||
splashScreen.SetIndeterminate();
|
||||
splashScreen.UpdateText(Key.SplashScreen_ShutdownProcedure);
|
||||
splashScreen.UpdateText(TextKey.SplashScreen_ShutdownProcedure);
|
||||
splashScreen.InvokeShow();
|
||||
}
|
||||
|
||||
private void LogAndShowException(Exception e)
|
||||
{
|
||||
logger.Error($"Failed to finalize application!", e);
|
||||
uiFactory.Show(text.Get(Key.MessageBox_ShutdownError), text.Get(Key.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error);
|
||||
uiFactory.Show(text.Get(TextKey.MessageBox_ShutdownError), text.Get(TextKey.MessageBox_ShutdownErrorTitle), icon: MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
private void Finish(bool success = true)
|
||||
|
|
|
@ -92,14 +92,14 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
|
||||
splashScreen = uiFactory.CreateSplashScreen(settings, text);
|
||||
splashScreen.SetMaxProgress(operationCount);
|
||||
splashScreen.UpdateText(Key.SplashScreen_StartupProcedure);
|
||||
splashScreen.UpdateText(TextKey.SplashScreen_StartupProcedure);
|
||||
splashScreen.InvokeShow();
|
||||
}
|
||||
|
||||
private void LogAndShowException(Exception e)
|
||||
{
|
||||
logger.Error($"Failed to initialize application!", e);
|
||||
uiFactory.Show(text.Get(Key.MessageBox_StartupError), text.Get(Key.MessageBox_StartupErrorTitle), icon: MessageBoxIcon.Error);
|
||||
uiFactory.Show(text.Get(TextKey.MessageBox_StartupError), text.Get(TextKey.MessageBox_StartupErrorTitle), icon: MessageBoxIcon.Error);
|
||||
logger.Info("Reverting operations...");
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace SafeExamBrowser.Core.I18n
|
|||
{
|
||||
public class Text : IText
|
||||
{
|
||||
private readonly IDictionary<Key, string> cache;
|
||||
private readonly IDictionary<TextKey, string> cache;
|
||||
|
||||
public Text(ITextResource resource)
|
||||
{
|
||||
|
@ -23,10 +23,10 @@ namespace SafeExamBrowser.Core.I18n
|
|||
throw new ArgumentNullException(nameof(resource));
|
||||
}
|
||||
|
||||
cache = resource.LoadText() ?? new Dictionary<Key, string>();
|
||||
cache = resource.LoadText() ?? new Dictionary<TextKey, string>();
|
||||
}
|
||||
|
||||
public string Get(Key key)
|
||||
public string Get(TextKey key)
|
||||
{
|
||||
return cache.ContainsKey(key) ? cache[key] : $"Could not find string for key '{key}'!";
|
||||
}
|
||||
|
|
|
@ -17,16 +17,16 @@ namespace SafeExamBrowser.Core.I18n
|
|||
{
|
||||
public class XmlTextResource : ITextResource
|
||||
{
|
||||
public IDictionary<Key, string> LoadText()
|
||||
public IDictionary<TextKey, string> LoadText()
|
||||
{
|
||||
var assembly = Assembly.GetAssembly(typeof(XmlTextResource)).Location;
|
||||
var path = Path.GetDirectoryName(assembly) + $@"\{nameof(I18n)}\Text.xml";
|
||||
var xml = XDocument.Load(path);
|
||||
var text = new Dictionary<Key, string>();
|
||||
var text = new Dictionary<TextKey, string>();
|
||||
|
||||
foreach (var definition in xml.Root.Descendants())
|
||||
{
|
||||
if (Enum.TryParse(definition.Name.LocalName, out Key key))
|
||||
if (Enum.TryParse(definition.Name.LocalName, out TextKey key))
|
||||
{
|
||||
text[key] = definition.Value;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace SafeExamBrowser.Core.Notifications
|
|||
{
|
||||
private IText text;
|
||||
|
||||
public string Tooltip => text.Get(Key.Notification_AboutTooltip);
|
||||
public string Tooltip => text.Get(TextKey.Notification_AboutTooltip);
|
||||
public IIconResource IconResource { get; } = new AboutNotificationIconResource();
|
||||
|
||||
public AboutNotificationInfo(IText text)
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace SafeExamBrowser.UserInterface
|
|||
private void InitializeAboutWindow()
|
||||
{
|
||||
Closing += (o, args) => closing?.Invoke();
|
||||
VersionInfo.Inlines.Add(new Run($"{text.Get(Key.Version)} {settings.ProgramVersion}") { FontStyle = FontStyles.Italic });
|
||||
VersionInfo.Inlines.Add(new Run($"{text.Get(TextKey.Version)} {settings.ProgramVersion}") { FontStyle = FontStyles.Italic });
|
||||
VersionInfo.Inlines.Add(new LineBreak());
|
||||
VersionInfo.Inlines.Add(new LineBreak());
|
||||
VersionInfo.Inlines.Add(new Run(settings.ProgramCopyright) { FontSize = 10 });
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace SafeExamBrowser.UserInterface
|
|||
model.MaxProgress = max;
|
||||
}
|
||||
|
||||
public void UpdateText(Key key, bool showBusyIndication = false)
|
||||
public void UpdateText(TextKey key, bool showBusyIndication = false)
|
||||
{
|
||||
model.StopBusyIndication();
|
||||
model.Status = text.Get(key);
|
||||
|
@ -73,7 +73,7 @@ namespace SafeExamBrowser.UserInterface
|
|||
|
||||
private void InitializeSplashScreen()
|
||||
{
|
||||
InfoTextBlock.Inlines.Add(new Run($"{text.Get(Key.Version)} {settings.ProgramVersion}") { FontStyle = FontStyles.Italic });
|
||||
InfoTextBlock.Inlines.Add(new Run($"{text.Get(TextKey.Version)} {settings.ProgramVersion}") { FontStyle = FontStyles.Italic });
|
||||
InfoTextBlock.Inlines.Add(new LineBreak());
|
||||
InfoTextBlock.Inlines.Add(new LineBreak());
|
||||
InfoTextBlock.Inlines.Add(new Run(settings.ProgramCopyright) { FontSize = 10 });
|
||||
|
|
|
@ -49,6 +49,22 @@ namespace SafeExamBrowser.WindowsApi.Constants
|
|||
/// </summary>
|
||||
internal const int WM_COMMAND = 0x111;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to the window with the keyboard focus when a nonsystem key is pressed. A nonsystem key is a key that is pressed when
|
||||
/// the ALT key is not pressed.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646280(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_KEYDOWN = 0x100;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to the window with the keyboard focus when a nonsystem key is released. A nonsystem key is a key that is pressed when
|
||||
/// the ALT key is not pressed, or a keyboard key that is pressed when a window has the keyboard focus.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646281(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_KEYUP = 0x101;
|
||||
|
||||
/// <summary>
|
||||
/// A window receives this message when the user chooses a command from the Window menu (formerly known as the system or control
|
||||
/// menu) or when the user chooses the maximize button, minimize button, restore button, or close button.
|
||||
|
@ -56,5 +72,25 @@ namespace SafeExamBrowser.WindowsApi.Constants
|
|||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646360(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_SYSCOMMAND = 0x112;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to the window with the keyboard focus when the user presses the F10 key (which activates the menu bar) or holds down
|
||||
/// the ALT key and then presses another key. It also occurs when no window currently has the keyboard focus; in this case, the
|
||||
/// WM_SYSKEYDOWN message is sent to the active window. The window that receives the message can distinguish between these two
|
||||
/// contexts by checking the context code in the lParam parameter.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646286(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_SYSKEYDOWN = 0x104;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to the window with the keyboard focus when the user releases a key that was pressed while the ALT key was held down.
|
||||
/// It also occurs when no window currently has the keyboard focus; in this case, the WM_SYSKEYUP message is sent to the active
|
||||
/// window. The window that receives the message can distinguish between these two contexts by checking the context code in the
|
||||
/// lParam parameter.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646287(v=vs.85).aspx
|
||||
/// </summary>
|
||||
internal const int WM_SYSKEYUP = 0x105;
|
||||
}
|
||||
}
|
||||
|
|
100
SafeExamBrowser.WindowsApi/Constants/HookType.cs
Normal file
100
SafeExamBrowser.WindowsApi/Constants/HookType.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2017 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.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Enums/HookType.html.
|
||||
/// </remarks>
|
||||
internal enum HookType
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that records input messages posted to the system message queue. This hook is useful for recording
|
||||
/// macros. For more information, see the JournalRecordProc hook procedure.
|
||||
/// </summary>
|
||||
WH_JOURNALRECORD = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that posts messages previously recorded by a WH_JOURNALRECORD hook procedure. For more information,
|
||||
/// see the JournalPlaybackProc hook procedure.
|
||||
/// </summary>
|
||||
WH_JOURNALPLAYBACK = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook procedure.
|
||||
/// </summary>
|
||||
WH_KEYBOARD = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors messages posted to a message queue. For more information, see the GetMsgProc hook
|
||||
/// procedure.
|
||||
/// </summary>
|
||||
WH_GETMESSAGE = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors messages before the system sends them to the destination window procedure. For more
|
||||
/// information, see the CallWndProc hook procedure.
|
||||
/// </summary>
|
||||
WH_CALLWNDPROC = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that receives notifications useful to a CBT application. For more information, see the CBTProc hook
|
||||
/// procedure.
|
||||
/// </summary>
|
||||
WH_CBT = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors messages generated as a result of an input event in a dialog box, message box, menu,
|
||||
/// or scroll bar. The hook procedure monitors these messages for all applications in the same desktop as the calling thread. For
|
||||
/// more information, see the SysMsgProc hook procedure.
|
||||
/// </summary>
|
||||
WH_SYSMSGFILTER = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure.
|
||||
/// </summary>
|
||||
WH_MOUSE = 7,
|
||||
|
||||
WH_HARDWARE = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure useful for debugging other hook procedures. For more information, see the DebugProc hook procedure.
|
||||
/// </summary>
|
||||
WH_DEBUG = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that receives notifications useful to shell applications. For more information, see the ShellProc
|
||||
/// hook procedure.
|
||||
/// </summary>
|
||||
WH_SHELL = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that will be called when the application's foreground thread is about to become idle. This hook is
|
||||
/// useful for performing low priority tasks during idle time. For more information, see the ForegroundIdleProc hook procedure.
|
||||
/// </summary>
|
||||
WH_FOREGROUNDIDLE = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors messages after they have been processed by the destination window procedure. For more
|
||||
/// information, see the CallWndRetProc hook procedure.
|
||||
/// </summary>
|
||||
WH_CALLWNDPROCRET = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors low-level keyboard input events. For more information, see the LowLevelKeyboardProc
|
||||
/// hook procedure.
|
||||
/// </summary>
|
||||
WH_KEYBOARD_LL = 13,
|
||||
|
||||
/// <summary>
|
||||
/// Installs a hook procedure that monitors low-level mouse input events. For more information, see the LowLevelMouseProc hook
|
||||
/// procedure.
|
||||
/// </summary>
|
||||
WH_MOUSE_LL = 14
|
||||
}
|
||||
}
|
48
SafeExamBrowser.WindowsApi/Constants/KBDLLHOOKSTRUCT.cs
Normal file
48
SafeExamBrowser.WindowsApi/Constants/KBDLLHOOKSTRUCT.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2017 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;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Structures/KBDLLHOOKSTRUCT.html.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KBDLLHOOKSTRUCT
|
||||
{
|
||||
/// <summary>
|
||||
/// A virtual-key code. The code must be a value in the range 1 to 254.
|
||||
/// </summary>
|
||||
public uint KeyCode;
|
||||
|
||||
/// <summary>
|
||||
/// A hardware scan code for the key.
|
||||
/// </summary>
|
||||
public uint ScanCode;
|
||||
|
||||
/// <summary>
|
||||
/// The extended-key flag, event-injected flags, context code, and transition-state flag. This member is specified as follows. An
|
||||
/// application can use the following values to test the keystroke flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the
|
||||
/// event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected
|
||||
/// from a process running at lower integrity level.
|
||||
/// </summary>
|
||||
public KBDLLHOOKSTRUCTFlags Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The time stamp for this message, equivalent to what <c>GetMessageTime</c> would return for this message.
|
||||
/// </summary>
|
||||
public uint Time;
|
||||
|
||||
/// <summary>
|
||||
/// Additional information associated with the message.
|
||||
/// </summary>
|
||||
public IntPtr DwExtraInfo;
|
||||
}
|
||||
}
|
36
SafeExamBrowser.WindowsApi/Constants/KBDLLHOOKSTRUCTFlags.cs
Normal file
36
SafeExamBrowser.WindowsApi/Constants/KBDLLHOOKSTRUCTFlags.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2017 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.WindowsApi.Constants
|
||||
{
|
||||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Structures/KBDLLHOOKSTRUCT.html.
|
||||
/// </remarks>
|
||||
public enum KBDLLHOOKSTRUCTFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Test the extended-key flag.
|
||||
/// </summary>
|
||||
LLKHF_EXTENDED = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Test the event-injected (from any process) flag.
|
||||
/// </summary>
|
||||
LLKHF_INJECTED = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// Test the context code.
|
||||
/// </summary>
|
||||
LLKHF_ALTDOWN = 0x20,
|
||||
|
||||
/// <summary>
|
||||
/// Test the transition-state flag.
|
||||
/// </summary>
|
||||
LLKHF_UP = 0x80
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ namespace SafeExamBrowser.WindowsApi.Constants
|
|||
|
||||
/// <summary>
|
||||
/// Activates the window and displays it as a maximized window.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
ShowMaximized = 3,
|
||||
|
||||
/// <summary>
|
||||
|
|
22
SafeExamBrowser.WindowsApi/Kernel32.cs
Normal file
22
SafeExamBrowser.WindowsApi/Kernel32.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2017 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;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the native Windows API exposed by <c>kernel32.dll</c>.
|
||||
/// </summary>
|
||||
internal class Kernel32
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||
}
|
||||
}
|
71
SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs
Normal file
71
SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2017 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 SafeExamBrowser.Contracts.Monitoring;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Monitoring
|
||||
{
|
||||
internal class KeyboardHook
|
||||
{
|
||||
internal IntPtr Handle { get; private set; }
|
||||
internal IKeyboardInterceptor Interceptor { get; private set; }
|
||||
|
||||
internal KeyboardHook(IKeyboardInterceptor interceptor)
|
||||
{
|
||||
Interceptor = interceptor;
|
||||
}
|
||||
|
||||
internal void Attach()
|
||||
{
|
||||
var module = Kernel32.GetModuleHandle(null);
|
||||
|
||||
Handle = User32.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, LowLevelKeyboardProc, module, 0);
|
||||
}
|
||||
|
||||
internal bool Detach()
|
||||
{
|
||||
return User32.UnhookWindowsHookEx(Handle);
|
||||
}
|
||||
|
||||
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0)
|
||||
{
|
||||
var keyData = (KBDLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
|
||||
var modifier = GetModifiers(keyData);
|
||||
|
||||
if (Interceptor.Block(keyData.KeyCode, modifier))
|
||||
{
|
||||
return (IntPtr) 1;
|
||||
}
|
||||
}
|
||||
|
||||
return User32.CallNextHookEx(Handle, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private KeyModifier GetModifiers(KBDLLHOOKSTRUCT keyData)
|
||||
{
|
||||
var modifier = KeyModifier.None;
|
||||
|
||||
if ((keyData.Flags & KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN) == KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN)
|
||||
{
|
||||
modifier |= KeyModifier.Alt;
|
||||
}
|
||||
|
||||
if (keyData.Flags == 0)
|
||||
{
|
||||
modifier |= KeyModifier.Ctrl;
|
||||
}
|
||||
|
||||
return modifier;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,17 +10,37 @@ using System;
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
using SafeExamBrowser.Contracts.WindowsApi;
|
||||
using SafeExamBrowser.Contracts.WindowsApi.Types;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Monitoring;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
public class NativeMethods : INativeMethods
|
||||
{
|
||||
private ConcurrentDictionary<IntPtr, WinEventDelegate> EventDelegates = new ConcurrentDictionary<IntPtr, WinEventDelegate>();
|
||||
private ConcurrentDictionary<IntPtr, EventProc> EventDelegates = new ConcurrentDictionary<IntPtr, EventProc>();
|
||||
private ConcurrentDictionary<IntPtr, KeyboardHook> KeyboardHooks = new ConcurrentDictionary<IntPtr, KeyboardHook>();
|
||||
|
||||
/// <summary>
|
||||
/// Upon finalization, unregister all system events and hooks...
|
||||
/// </summary>
|
||||
~NativeMethods()
|
||||
{
|
||||
foreach (var handle in EventDelegates.Keys)
|
||||
{
|
||||
User32.UnhookWinEvent(handle);
|
||||
}
|
||||
|
||||
foreach (var handle in KeyboardHooks.Keys)
|
||||
{
|
||||
User32.UnhookWindowsHookEx(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IntPtr> GetOpenWindows()
|
||||
{
|
||||
|
@ -118,9 +138,21 @@ namespace SafeExamBrowser.WindowsApi
|
|||
}
|
||||
}
|
||||
|
||||
public void RegisterKeyboardHook(IKeyboardInterceptor interceptor)
|
||||
{
|
||||
var hook = new KeyboardHook(interceptor);
|
||||
|
||||
hook.Attach();
|
||||
|
||||
// IMORTANT:
|
||||
// Ensures that the hook 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!
|
||||
KeyboardHooks[hook.Handle] = hook;
|
||||
}
|
||||
|
||||
public IntPtr RegisterSystemForegroundEvent(Action<IntPtr> callback)
|
||||
{
|
||||
WinEventDelegate eventProc = (IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) =>
|
||||
EventProc eventProc = (IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) =>
|
||||
{
|
||||
callback(hwnd);
|
||||
};
|
||||
|
@ -128,7 +160,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
var handle = User32.SetWinEventHook(Constant.EVENT_SYSTEM_FOREGROUND, Constant.EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, eventProc, 0, 0, Constant.WINEVENT_OUTOFCONTEXT);
|
||||
|
||||
// IMORTANT:
|
||||
// Ensures that the callback does not get garbage collected prematurely, as it will be passed to unmanaged code.
|
||||
// Ensures that the event 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!
|
||||
EventDelegates[handle] = eventProc;
|
||||
|
||||
|
@ -137,7 +169,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
|
||||
public IntPtr RegisterSystemCaptureStartEvent(Action<IntPtr> callback)
|
||||
{
|
||||
WinEventDelegate eventProc = (IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) =>
|
||||
EventProc eventProc = (IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) =>
|
||||
{
|
||||
callback(hwnd);
|
||||
};
|
||||
|
@ -145,7 +177,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
var handle = User32.SetWinEventHook(Constant.EVENT_SYSTEM_CAPTURESTART, Constant.EVENT_SYSTEM_CAPTURESTART, IntPtr.Zero, eventProc, 0, 0, Constant.WINEVENT_OUTOFCONTEXT);
|
||||
|
||||
// IMORTANT:
|
||||
// Ensures that the callback does not get garbage collected prematurely, as it will be passed to unmanaged code.
|
||||
// Ensures that the event 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!
|
||||
EventDelegates[handle] = eventProc;
|
||||
|
||||
|
@ -172,6 +204,23 @@ namespace SafeExamBrowser.WindowsApi
|
|||
}
|
||||
}
|
||||
|
||||
public void UnregisterKeyboardHook(IKeyboardInterceptor interceptor)
|
||||
{
|
||||
var hook = KeyboardHooks.Values.FirstOrDefault(h => h.Interceptor == interceptor);
|
||||
|
||||
if (hook != null)
|
||||
{
|
||||
var success = hook.Detach();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
KeyboardHooks.TryRemove(hook.Handle, out KeyboardHook h);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnregisterSystemEvent(IntPtr handle)
|
||||
{
|
||||
var success = User32.UnhookWinEvent(handle);
|
||||
|
@ -180,10 +229,8 @@ namespace SafeExamBrowser.WindowsApi
|
|||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
else
|
||||
{
|
||||
EventDelegates.TryRemove(handle, out WinEventDelegate d);
|
||||
}
|
||||
|
||||
EventDelegates.TryRemove(handle, out EventProc d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,8 +59,13 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Constants\Constant.cs" />
|
||||
<Compile Include="Constants\HookType.cs" />
|
||||
<Compile Include="Constants\KBDLLHOOKSTRUCT.cs" />
|
||||
<Compile Include="Constants\KBDLLHOOKSTRUCTFlags.cs" />
|
||||
<Compile Include="Constants\ShowWindowCommand.cs" />
|
||||
<Compile Include="Constants\SystemCommand.cs" />
|
||||
<Compile Include="Kernel32.cs" />
|
||||
<Compile Include="Monitoring\KeyboardHook.cs" />
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Constants\SPI.cs" />
|
||||
|
|
|
@ -15,13 +15,17 @@ using SafeExamBrowser.WindowsApi.Constants;
|
|||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
internal delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lParam);
|
||||
internal delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
|
||||
internal delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
|
||||
internal delegate void EventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to the native Windows API exposed by <c>user32.dll</c>.
|
||||
/// </summary>
|
||||
internal static class User32
|
||||
{
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool EnumWindows(EnumWindowsDelegate enumProc, IntPtr lParam);
|
||||
|
@ -50,7 +54,10 @@ namespace SafeExamBrowser.WindowsApi
|
|||
internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
|
||||
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, EventProc lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
|
@ -62,5 +69,9 @@ namespace SafeExamBrowser.WindowsApi
|
|||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue