Implemented basic mouse interception.
This commit is contained in:
parent
a79f0c6d77
commit
0807b5f5f9
23 changed files with 452 additions and 61 deletions
|
@ -63,6 +63,7 @@
|
|||
<Compile Include="Bounds.cs" />
|
||||
<Compile Include="Settings\BrowserSettings.cs" />
|
||||
<Compile Include="Settings\KeyboardSettings.cs" />
|
||||
<Compile Include="Settings\MouseSettings.cs" />
|
||||
<Compile Include="Settings\SettingsImpl.cs" />
|
||||
<Compile Include="WorkingArea.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
|
19
SafeExamBrowser.Configuration/Settings/MouseSettings.cs
Normal file
19
SafeExamBrowser.Configuration/Settings/MouseSettings.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
|
||||
namespace SafeExamBrowser.Configuration.Settings
|
||||
{
|
||||
public class MouseSettings : IMouseSettings
|
||||
{
|
||||
public bool AllowMiddleButton => false;
|
||||
|
||||
public bool AllowRightButton => false;
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ namespace SafeExamBrowser.Configuration
|
|||
{
|
||||
Browser = new BrowserSettings(this);
|
||||
Keyboard = new KeyboardSettings();
|
||||
Mouse = new MouseSettings();
|
||||
}
|
||||
|
||||
public string AppDataFolderName => "SafeExamBrowser";
|
||||
|
@ -38,6 +39,8 @@ namespace SafeExamBrowser.Configuration
|
|||
|
||||
public IKeyboardSettings Keyboard { get; private set; }
|
||||
|
||||
public IMouseSettings Mouse { get; private set; }
|
||||
|
||||
public string LogFolderPath
|
||||
{
|
||||
get { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), AppDataFolderName, "Logs"); }
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.Contracts.Configuration.Settings
|
||||
{
|
||||
public interface IMouseSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the user may use the middle mouse button.
|
||||
/// </summary>
|
||||
bool AllowMiddleButton { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user may use the right mouse button.
|
||||
/// </summary>
|
||||
bool AllowRightButton { get; }
|
||||
}
|
||||
}
|
|
@ -30,6 +30,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
|
|||
/// </summary>
|
||||
IKeyboardSettings Keyboard { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All mouse-related settings.
|
||||
/// </summary>
|
||||
IMouseSettings Mouse { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path where the log files are to be stored.
|
||||
/// </summary>
|
||||
|
|
|
@ -10,5 +10,9 @@ namespace SafeExamBrowser.Contracts.Monitoring
|
|||
{
|
||||
public interface IMouseInterceptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns <c>true</c> if the given button should be blocked, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool Block(MouseButton button, KeyState state);
|
||||
}
|
||||
}
|
||||
|
|
18
SafeExamBrowser.Contracts/Monitoring/MouseButton.cs
Normal file
18
SafeExamBrowser.Contracts/Monitoring/MouseButton.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.Contracts.Monitoring
|
||||
{
|
||||
public enum MouseButton
|
||||
{
|
||||
None = 0,
|
||||
Left,
|
||||
Middle,
|
||||
Right
|
||||
}
|
||||
}
|
|
@ -68,6 +68,7 @@
|
|||
<Compile Include="Configuration\INotificationInfo.cs" />
|
||||
<Compile Include="Configuration\Settings\IBrowserSettings.cs" />
|
||||
<Compile Include="Configuration\Settings\IKeyboardSettings.cs" />
|
||||
<Compile Include="Configuration\Settings\IMouseSettings.cs" />
|
||||
<Compile Include="Configuration\Settings\ISettings.cs" />
|
||||
<Compile Include="Behaviour\IShutdownController.cs" />
|
||||
<Compile Include="Behaviour\IStartupController.cs" />
|
||||
|
@ -87,6 +88,7 @@
|
|||
<Compile Include="Monitoring\IWindowMonitor.cs" />
|
||||
<Compile Include="Monitoring\KeyModifier.cs" />
|
||||
<Compile Include="Monitoring\KeyState.cs" />
|
||||
<Compile Include="Monitoring\MouseButton.cs" />
|
||||
<Compile Include="UserInterface\IBrowserControl.cs" />
|
||||
<Compile Include="UserInterface\IBrowserWindow.cs" />
|
||||
<Compile Include="UserInterface\IMessageBox.cs" />
|
||||
|
|
|
@ -14,6 +14,30 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
|||
{
|
||||
public interface INativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// Deregisters 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 DeregisterKeyboardHook(IKeyboardInterceptor interceptor);
|
||||
|
||||
/// <summary>
|
||||
/// Deregisters the system hook for the given mouse interceptor.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">
|
||||
/// If the hook for the given interceptor could not be successfully removed.
|
||||
/// </exception>
|
||||
void DeregisterMouseHook(IMouseInterceptor interceptor);
|
||||
|
||||
/// <summary>
|
||||
/// Deregisters a previously registered system event.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">
|
||||
/// If the event hook could not be successfully removed.
|
||||
/// </exception>
|
||||
void DeregisterSystemEvent(IntPtr handle);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a collection of handles to all currently open (i.e. visible) windows.
|
||||
/// </summary>
|
||||
|
@ -76,6 +100,11 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
|||
/// </summary>
|
||||
void RegisterKeyboardHook(IKeyboardInterceptor interceptor);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a system hook for the given mouse interceptor.
|
||||
/// </summary>
|
||||
void RegisterMouseHook(IMouseInterceptor 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.
|
||||
|
@ -105,21 +134,5 @@ namespace SafeExamBrowser.Contracts.WindowsApi
|
|||
/// If the working area could not be set.
|
||||
/// </exception>
|
||||
void SetWorkingArea(IBounds 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>
|
||||
/// <exception cref="System.ComponentModel.Win32Exception">
|
||||
/// If the event hook could not be successfully removed.
|
||||
/// </exception>
|
||||
void UnregisterSystemEvent(IntPtr handle);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,14 +18,20 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
{
|
||||
private IKeyboardInterceptor keyboardInterceptor;
|
||||
private ILogger logger;
|
||||
private IMouseInterceptor mouseInterceptor;
|
||||
private INativeMethods nativeMethods;
|
||||
|
||||
public ISplashScreen SplashScreen { private get; set; }
|
||||
|
||||
public DeviceInterceptionOperation(IKeyboardInterceptor keyboardInterceptor, ILogger logger, INativeMethods nativeMethods)
|
||||
public DeviceInterceptionOperation(
|
||||
IKeyboardInterceptor keyboardInterceptor,
|
||||
ILogger logger,
|
||||
IMouseInterceptor mouseInterceptor,
|
||||
INativeMethods nativeMethods)
|
||||
{
|
||||
this.keyboardInterceptor = keyboardInterceptor;
|
||||
this.logger = logger;
|
||||
this.mouseInterceptor = mouseInterceptor;
|
||||
this.nativeMethods = nativeMethods;
|
||||
}
|
||||
|
||||
|
@ -34,13 +40,15 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
|
|||
logger.Info("Starting keyboard and mouse interception...");
|
||||
|
||||
nativeMethods.RegisterKeyboardHook(keyboardInterceptor);
|
||||
nativeMethods.RegisterMouseHook(mouseInterceptor);
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
{
|
||||
logger.Info("Stopping keyboard and mouse interception...");
|
||||
|
||||
nativeMethods.UnregisterKeyboardHook(keyboardInterceptor);
|
||||
nativeMethods.DeregisterMouseHook(mouseInterceptor);
|
||||
nativeMethods.DeregisterKeyboardHook(keyboardInterceptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
41
SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs
Normal file
41
SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.Mouse
|
||||
{
|
||||
public class MouseInterceptor : IMouseInterceptor
|
||||
{
|
||||
private ILogger logger;
|
||||
private IMouseSettings settings;
|
||||
|
||||
public MouseInterceptor(ILogger logger, IMouseSettings settings)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public bool Block(MouseButton button, KeyState state)
|
||||
{
|
||||
var block = false;
|
||||
|
||||
block |= !settings.AllowMiddleButton && button == MouseButton.Middle;
|
||||
block |= !settings.AllowRightButton && button == MouseButton.Right;
|
||||
|
||||
if (block)
|
||||
{
|
||||
logger.Info($"Blocked {button.ToString().ToLower()} mouse button when {state.ToString().ToLower()}.");
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -61,6 +61,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Keyboard\KeyboardInterceptor.cs" />
|
||||
<Compile Include="Mouse\MouseInterceptor.cs" />
|
||||
<Compile Include="Processes\ProcessMonitor.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Windows\WindowMonitor.cs" />
|
||||
|
@ -71,8 +72,6 @@
|
|||
<Name>SafeExamBrowser.Contracts</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Mouse\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -102,13 +102,13 @@ namespace SafeExamBrowser.Monitoring.Windows
|
|||
{
|
||||
if (captureStartHookHandle != IntPtr.Zero)
|
||||
{
|
||||
nativeMethods.UnregisterSystemEvent(captureStartHookHandle);
|
||||
nativeMethods.DeregisterSystemEvent(captureStartHookHandle);
|
||||
logger.Info($"Unregistered system capture start event with handle = {captureStartHookHandle}.");
|
||||
}
|
||||
|
||||
if (foregroundHookHandle != IntPtr.Zero)
|
||||
{
|
||||
nativeMethods.UnregisterSystemEvent(foregroundHookHandle);
|
||||
nativeMethods.DeregisterSystemEvent(foregroundHookHandle);
|
||||
logger.Info($"Unregistered system foreground event with handle = {foregroundHookHandle}.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,77 @@ namespace SafeExamBrowser.WindowsApi.Constants
|
|||
/// </summary>
|
||||
internal const int WM_KEYUP = 0x101;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user presses the left mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645607(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_LBUTTONDOWN = 0x201;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user releases the left mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645608(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_LBUTTONUP = 0x202;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user presses the middle mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645610(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_MBUTTONDOWN = 0x207;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user releases the middle mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645611(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_MBUTTONUP = 0x208;
|
||||
|
||||
/// <summary>
|
||||
/// Posted to a window when the cursor moves. If the mouse is not captured, the message is posted to the window that contains the
|
||||
/// cursor. Otherwise, the message is posted to the window that has captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645616(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_MOUSEMOVE = 0x200;
|
||||
|
||||
/// <summary>
|
||||
/// Sent to the focus window when the mouse wheel is rotated. The DefWindowProc function propagates the message to the window's
|
||||
/// parent. There should be no internal forwarding of the message, since DefWindowProc propagates it up the parent chain until i
|
||||
/// finds a window that processes it.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_MOUSEWHEEL = 0x20A;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user presses the right mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646242(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_RBUTTONDOWN = 0x204;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user releases the right mouse button while the cursor is in the client area of a window. If the mouse is not
|
||||
/// captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646243(v=vs.85).aspx.
|
||||
/// </summary>
|
||||
internal const int WM_RBUTTONUP = 0x205;
|
||||
|
||||
/// <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.
|
||||
|
|
|
@ -54,9 +54,9 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
|
|||
{
|
||||
if (nCode >= 0)
|
||||
{
|
||||
var state = GetState(wParam.ToInt32());
|
||||
var keyData = (KBDLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
|
||||
var modifier = GetModifiers(keyData, wParam.ToInt32());
|
||||
var state = GetState(wParam.ToInt32());
|
||||
|
||||
if (Interceptor.Block((int) keyData.KeyCode, modifier, state))
|
||||
{
|
||||
|
|
104
SafeExamBrowser.WindowsApi/Monitoring/MouseHook.cs
Normal file
104
SafeExamBrowser.WindowsApi/Monitoring/MouseHook.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Monitoring
|
||||
{
|
||||
internal class MouseHook
|
||||
{
|
||||
private HookProc hookProc;
|
||||
|
||||
internal IntPtr Handle { get; private set; }
|
||||
internal IMouseInterceptor Interceptor { get; private set; }
|
||||
|
||||
internal MouseHook(IMouseInterceptor interceptor)
|
||||
{
|
||||
Interceptor = interceptor;
|
||||
}
|
||||
|
||||
internal void Attach()
|
||||
{
|
||||
var module = Kernel32.GetModuleHandle(null);
|
||||
|
||||
// IMORTANT:
|
||||
// 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!
|
||||
hookProc = new HookProc(LowLevelMouseProc);
|
||||
|
||||
Handle = User32.SetWindowsHookEx(HookType.WH_MOUSE_LL, hookProc, module, 0);
|
||||
}
|
||||
|
||||
internal bool Detach()
|
||||
{
|
||||
return User32.UnhookWindowsHookEx(Handle);
|
||||
}
|
||||
|
||||
private IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0 && !Ignore(wParam.ToInt32()))
|
||||
{
|
||||
var mouseData = (MSLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
|
||||
var button = GetButton(wParam.ToInt32());
|
||||
var state = GetState(wParam.ToInt32());
|
||||
|
||||
if (Interceptor.Block(button, state))
|
||||
{
|
||||
return (IntPtr) 1;
|
||||
}
|
||||
}
|
||||
|
||||
return User32.CallNextHookEx(Handle, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private bool Ignore(int wParam)
|
||||
{
|
||||
// For performance reasons, ignore mouse movement and wheel rotation...
|
||||
return wParam == Constant.WM_MOUSEMOVE || wParam == Constant.WM_MOUSEWHEEL;
|
||||
}
|
||||
|
||||
private MouseButton GetButton(int wParam)
|
||||
{
|
||||
switch (wParam)
|
||||
{
|
||||
case Constant.WM_LBUTTONDOWN:
|
||||
case Constant.WM_LBUTTONUP:
|
||||
return MouseButton.Left;
|
||||
case Constant.WM_MBUTTONDOWN:
|
||||
case Constant.WM_MBUTTONUP:
|
||||
return MouseButton.Middle;
|
||||
case Constant.WM_RBUTTONDOWN:
|
||||
case Constant.WM_RBUTTONUP:
|
||||
return MouseButton.Right;
|
||||
default:
|
||||
return MouseButton.None;
|
||||
}
|
||||
}
|
||||
|
||||
private KeyState GetState(int wParam)
|
||||
{
|
||||
switch (wParam)
|
||||
{
|
||||
case Constant.WM_LBUTTONDOWN:
|
||||
case Constant.WM_MBUTTONDOWN:
|
||||
case Constant.WM_RBUTTONDOWN:
|
||||
return KeyState.Pressed;
|
||||
case Constant.WM_LBUTTONUP:
|
||||
case Constant.WM_MBUTTONUP:
|
||||
case Constant.WM_RBUTTONUP:
|
||||
return KeyState.Released;
|
||||
default:
|
||||
return KeyState.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
{
|
||||
private ConcurrentDictionary<IntPtr, EventProc> EventDelegates = new ConcurrentDictionary<IntPtr, EventProc>();
|
||||
private ConcurrentDictionary<IntPtr, KeyboardHook> KeyboardHooks = new ConcurrentDictionary<IntPtr, KeyboardHook>();
|
||||
private ConcurrentDictionary<IntPtr, MouseHook> MouseHooks = new ConcurrentDictionary<IntPtr, MouseHook>();
|
||||
|
||||
/// <summary>
|
||||
/// Upon finalization, unregister all active system events and hooks...
|
||||
|
@ -40,6 +41,57 @@ namespace SafeExamBrowser.WindowsApi
|
|||
{
|
||||
User32.UnhookWindowsHookEx(handle);
|
||||
}
|
||||
|
||||
foreach (var handle in MouseHooks.Keys)
|
||||
{
|
||||
User32.UnhookWindowsHookEx(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeregisterKeyboardHook(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 DeregisterMouseHook(IMouseInterceptor interceptor)
|
||||
{
|
||||
var hook = MouseHooks.Values.FirstOrDefault(h => h.Interceptor == interceptor);
|
||||
|
||||
if (hook != null)
|
||||
{
|
||||
var success = hook.Detach();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
MouseHooks.TryRemove(hook.Handle, out MouseHook h);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeregisterSystemEvent(IntPtr handle)
|
||||
{
|
||||
var success = User32.UnhookWinEvent(handle);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
EventDelegates.TryRemove(handle, out EventProc d);
|
||||
}
|
||||
|
||||
public IEnumerable<IntPtr> GetOpenWindows()
|
||||
|
@ -147,6 +199,15 @@ namespace SafeExamBrowser.WindowsApi
|
|||
KeyboardHooks[hook.Handle] = hook;
|
||||
}
|
||||
|
||||
public void RegisterMouseHook(IMouseInterceptor interceptor)
|
||||
{
|
||||
var hook = new MouseHook(interceptor);
|
||||
|
||||
hook.Attach();
|
||||
|
||||
MouseHooks[hook.Handle] = hook;
|
||||
}
|
||||
|
||||
public IntPtr RegisterSystemForegroundEvent(Action<IntPtr> callback)
|
||||
{
|
||||
EventProc eventProc = (IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) =>
|
||||
|
@ -201,34 +262,5 @@ namespace SafeExamBrowser.WindowsApi
|
|||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
EventDelegates.TryRemove(handle, out EventProc d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Constants\Constant.cs" />
|
||||
<Compile Include="Constants\HookType.cs" />
|
||||
<Compile Include="Monitoring\MouseHook.cs" />
|
||||
<Compile Include="Types\Bounds.cs" />
|
||||
<Compile Include="Types\KBDLLHOOKSTRUCT.cs" />
|
||||
<Compile Include="Types\KBDLLHOOKSTRUCTFlags.cs" />
|
||||
|
@ -71,6 +72,8 @@
|
|||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Constants\SPI.cs" />
|
||||
<Compile Include="Constants\SPIF.cs" />
|
||||
<Compile Include="Types\MSLLHOOKSTRUCT.cs" />
|
||||
<Compile Include="Types\POINT.cs" />
|
||||
<Compile Include="Types\RECT.cs" />
|
||||
<Compile Include="User32.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -15,17 +15,17 @@ namespace SafeExamBrowser.WindowsApi.Types
|
|||
/// See http://www.pinvoke.net/default.aspx/Structures/KBDLLHOOKSTRUCT.html.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KBDLLHOOKSTRUCT
|
||||
internal struct KBDLLHOOKSTRUCT
|
||||
{
|
||||
/// <summary>
|
||||
/// A virtual-key code. The code must be a value in the range 1 to 254.
|
||||
/// </summary>
|
||||
public uint KeyCode;
|
||||
internal uint KeyCode;
|
||||
|
||||
/// <summary>
|
||||
/// A hardware scan code for the key.
|
||||
/// </summary>
|
||||
public uint ScanCode;
|
||||
internal uint ScanCode;
|
||||
|
||||
/// <summary>
|
||||
/// The extended-key flag, event-injected flags, context code, and transition-state flag. This member is specified as follows. An
|
||||
|
@ -33,16 +33,16 @@ namespace SafeExamBrowser.WindowsApi.Types
|
|||
/// 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;
|
||||
internal KBDLLHOOKSTRUCTFlags Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The time stamp for this message, equivalent to what <c>GetMessageTime</c> would return for this message.
|
||||
/// </summary>
|
||||
public uint Time;
|
||||
internal uint Time;
|
||||
|
||||
/// <summary>
|
||||
/// Additional information associated with the message.
|
||||
/// </summary>
|
||||
public IntPtr DwExtraInfo;
|
||||
internal IntPtr DwExtraInfo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace SafeExamBrowser.WindowsApi.Types
|
|||
/// <remarks>
|
||||
/// See http://www.pinvoke.net/default.aspx/Structures/KBDLLHOOKSTRUCT.html.
|
||||
/// </remarks>
|
||||
public enum KBDLLHOOKSTRUCTFlags
|
||||
internal enum KBDLLHOOKSTRUCTFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// Test the extended-key flag.
|
||||
|
|
23
SafeExamBrowser.WindowsApi/Types/MSLLHOOKSTRUCT.cs
Normal file
23
SafeExamBrowser.WindowsApi/Types/MSLLHOOKSTRUCT.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct MSLLHOOKSTRUCT
|
||||
{
|
||||
internal POINT Point;
|
||||
internal int MouseData;
|
||||
internal int Flags;
|
||||
internal int Time;
|
||||
internal UIntPtr DwExtraInfo;
|
||||
}
|
||||
}
|
19
SafeExamBrowser.WindowsApi/Types/POINT.cs
Normal file
19
SafeExamBrowser.WindowsApi/Types/POINT.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.Runtime.InteropServices;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi.Types
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct POINT
|
||||
{
|
||||
internal int X;
|
||||
internal int Y;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ using SafeExamBrowser.Core.Behaviour.Operations;
|
|||
using SafeExamBrowser.Core.I18n;
|
||||
using SafeExamBrowser.Core.Logging;
|
||||
using SafeExamBrowser.Monitoring.Keyboard;
|
||||
using SafeExamBrowser.Monitoring.Mouse;
|
||||
using SafeExamBrowser.Monitoring.Processes;
|
||||
using SafeExamBrowser.Monitoring.Windows;
|
||||
using SafeExamBrowser.UserInterface;
|
||||
|
@ -35,6 +36,7 @@ namespace SafeExamBrowser
|
|||
private IApplicationInfo browserInfo;
|
||||
private IKeyboardInterceptor keyboardInterceptor;
|
||||
private ILogger logger;
|
||||
private IMouseInterceptor mouseInterceptor;
|
||||
private INativeMethods nativeMethods;
|
||||
private IProcessMonitor processMonitor;
|
||||
private IRuntimeController runtimeController;
|
||||
|
@ -65,6 +67,7 @@ namespace SafeExamBrowser
|
|||
text = new Text(textResource);
|
||||
browserController = new BrowserApplicationController(settings, text, uiFactory);
|
||||
keyboardInterceptor = new KeyboardInterceptor(settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor)));
|
||||
mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), settings.Mouse);
|
||||
processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
|
||||
windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods);
|
||||
workingArea = new WorkingArea(new ModuleLogger(logger, typeof(WorkingArea)), nativeMethods);
|
||||
|
@ -74,7 +77,7 @@ namespace SafeExamBrowser
|
|||
StartupController = new StartupController(logger, settings, text, uiFactory);
|
||||
|
||||
StartupOperations = new Queue<IOperation>();
|
||||
StartupOperations.Enqueue(new DeviceInterceptionOperation(keyboardInterceptor, logger, nativeMethods));
|
||||
StartupOperations.Enqueue(new DeviceInterceptionOperation(keyboardInterceptor, logger, mouseInterceptor, nativeMethods));
|
||||
StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
|
||||
StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
|
||||
StartupOperations.Enqueue(new WorkingAreaOperation(logger, Taskbar, workingArea));
|
||||
|
|
Loading…
Reference in a new issue