diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj index 7f770954..e263f7dc 100644 --- a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj +++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj @@ -63,6 +63,7 @@ + diff --git a/SafeExamBrowser.Configuration/Settings/MouseSettings.cs b/SafeExamBrowser.Configuration/Settings/MouseSettings.cs new file mode 100644 index 00000000..f911db24 --- /dev/null +++ b/SafeExamBrowser.Configuration/Settings/MouseSettings.cs @@ -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; + } +} diff --git a/SafeExamBrowser.Configuration/Settings/SettingsImpl.cs b/SafeExamBrowser.Configuration/Settings/SettingsImpl.cs index c0b95eb7..acce0f86 100644 --- a/SafeExamBrowser.Configuration/Settings/SettingsImpl.cs +++ b/SafeExamBrowser.Configuration/Settings/SettingsImpl.cs @@ -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"); } diff --git a/SafeExamBrowser.Contracts/Configuration/Settings/IMouseSettings.cs b/SafeExamBrowser.Contracts/Configuration/Settings/IMouseSettings.cs new file mode 100644 index 00000000..3697e0c0 --- /dev/null +++ b/SafeExamBrowser.Contracts/Configuration/Settings/IMouseSettings.cs @@ -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 + { + /// + /// Determines whether the user may use the middle mouse button. + /// + bool AllowMiddleButton { get; } + + /// + /// Determines whether the user may use the right mouse button. + /// + bool AllowRightButton { get; } + } +} diff --git a/SafeExamBrowser.Contracts/Configuration/Settings/ISettings.cs b/SafeExamBrowser.Contracts/Configuration/Settings/ISettings.cs index 2129ac26..855f291e 100644 --- a/SafeExamBrowser.Contracts/Configuration/Settings/ISettings.cs +++ b/SafeExamBrowser.Contracts/Configuration/Settings/ISettings.cs @@ -30,6 +30,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings /// IKeyboardSettings Keyboard { get; } + /// + /// All mouse-related settings. + /// + IMouseSettings Mouse { get; } + /// /// The path where the log files are to be stored. /// diff --git a/SafeExamBrowser.Contracts/Monitoring/IMouseInterceptor.cs b/SafeExamBrowser.Contracts/Monitoring/IMouseInterceptor.cs index a69d7f2e..fb74aa9a 100644 --- a/SafeExamBrowser.Contracts/Monitoring/IMouseInterceptor.cs +++ b/SafeExamBrowser.Contracts/Monitoring/IMouseInterceptor.cs @@ -10,5 +10,9 @@ namespace SafeExamBrowser.Contracts.Monitoring { public interface IMouseInterceptor { + /// + /// Returns true if the given button should be blocked, otherwise false. + /// + bool Block(MouseButton button, KeyState state); } } diff --git a/SafeExamBrowser.Contracts/Monitoring/MouseButton.cs b/SafeExamBrowser.Contracts/Monitoring/MouseButton.cs new file mode 100644 index 00000000..9b80cea5 --- /dev/null +++ b/SafeExamBrowser.Contracts/Monitoring/MouseButton.cs @@ -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 + } +} diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 2b11f3c8..87709b55 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -68,6 +68,7 @@ + @@ -87,6 +88,7 @@ + diff --git a/SafeExamBrowser.Contracts/WindowsApi/INativeMethods.cs b/SafeExamBrowser.Contracts/WindowsApi/INativeMethods.cs index 8cd0145c..2cf66181 100644 --- a/SafeExamBrowser.Contracts/WindowsApi/INativeMethods.cs +++ b/SafeExamBrowser.Contracts/WindowsApi/INativeMethods.cs @@ -14,6 +14,30 @@ namespace SafeExamBrowser.Contracts.WindowsApi { public interface INativeMethods { + /// + /// Deregisters the system hook for the given keyboard interceptor. + /// + /// + /// If the hook for the given interceptor could not be successfully removed. + /// + void DeregisterKeyboardHook(IKeyboardInterceptor interceptor); + + /// + /// Deregisters the system hook for the given mouse interceptor. + /// + /// + /// If the hook for the given interceptor could not be successfully removed. + /// + void DeregisterMouseHook(IMouseInterceptor interceptor); + + /// + /// Deregisters a previously registered system event. + /// + /// + /// If the event hook could not be successfully removed. + /// + void DeregisterSystemEvent(IntPtr handle); + /// /// Retrieves a collection of handles to all currently open (i.e. visible) windows. /// @@ -76,6 +100,11 @@ namespace SafeExamBrowser.Contracts.WindowsApi /// void RegisterKeyboardHook(IKeyboardInterceptor interceptor); + /// + /// Registers a system hook for the given mouse interceptor. + /// + void RegisterMouseHook(IMouseInterceptor interceptor); + /// /// 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. /// void SetWorkingArea(IBounds bounds); - - /// - /// Unregisters the system hook for the given keyboard interceptor. - /// - /// - /// If the hook for the given interceptor could not be successfully removed. - /// - void UnregisterKeyboardHook(IKeyboardInterceptor interceptor); - - /// - /// Unregisters a previously registered system event. - /// - /// - /// If the event hook could not be successfully removed. - /// - void UnregisterSystemEvent(IntPtr handle); } } diff --git a/SafeExamBrowser.Core/Behaviour/Operations/DeviceInterceptionOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/DeviceInterceptionOperation.cs index 7315d799..79ba4663 100644 --- a/SafeExamBrowser.Core/Behaviour/Operations/DeviceInterceptionOperation.cs +++ b/SafeExamBrowser.Core/Behaviour/Operations/DeviceInterceptionOperation.cs @@ -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); } } } diff --git a/SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs b/SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs new file mode 100644 index 00000000..77d3beef --- /dev/null +++ b/SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs @@ -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; + } + } +} diff --git a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj index c2e4117b..6f4187fe 100644 --- a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj +++ b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj @@ -61,6 +61,7 @@ + @@ -71,8 +72,6 @@ SafeExamBrowser.Contracts - - - + \ No newline at end of file diff --git a/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs b/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs index 1f170474..84256968 100644 --- a/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs +++ b/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs @@ -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}."); } } diff --git a/SafeExamBrowser.WindowsApi/Constants/Constant.cs b/SafeExamBrowser.WindowsApi/Constants/Constant.cs index cedc121f..a531b98a 100644 --- a/SafeExamBrowser.WindowsApi/Constants/Constant.cs +++ b/SafeExamBrowser.WindowsApi/Constants/Constant.cs @@ -65,6 +65,77 @@ namespace SafeExamBrowser.WindowsApi.Constants /// internal const int WM_KEYUP = 0x101; + /// + /// 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. + /// + internal const int WM_LBUTTONDOWN = 0x201; + + /// + /// 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. + /// + internal const int WM_LBUTTONUP = 0x202; + + /// + /// 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. + /// + internal const int WM_MBUTTONDOWN = 0x207; + + /// + /// 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. + /// + internal const int WM_MBUTTONUP = 0x208; + + /// + /// 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. + /// + internal const int WM_MOUSEMOVE = 0x200; + + /// + /// 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. + /// + internal const int WM_MOUSEWHEEL = 0x20A; + + /// + /// 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. + /// + internal const int WM_RBUTTONDOWN = 0x204; + + /// + /// 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. + /// + internal const int WM_RBUTTONUP = 0x205; + /// /// 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. diff --git a/SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs b/SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs index c9c5a837..f1a9c02d 100644 --- a/SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs +++ b/SafeExamBrowser.WindowsApi/Monitoring/KeyboardHook.cs @@ -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)) { diff --git a/SafeExamBrowser.WindowsApi/Monitoring/MouseHook.cs b/SafeExamBrowser.WindowsApi/Monitoring/MouseHook.cs new file mode 100644 index 00000000..3aa74f08 --- /dev/null +++ b/SafeExamBrowser.WindowsApi/Monitoring/MouseHook.cs @@ -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 CallbackOnCollectedDelegate 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; + } + } + } +} diff --git a/SafeExamBrowser.WindowsApi/NativeMethods.cs b/SafeExamBrowser.WindowsApi/NativeMethods.cs index 0914e39b..aac9aff1 100644 --- a/SafeExamBrowser.WindowsApi/NativeMethods.cs +++ b/SafeExamBrowser.WindowsApi/NativeMethods.cs @@ -25,6 +25,7 @@ namespace SafeExamBrowser.WindowsApi { private ConcurrentDictionary EventDelegates = new ConcurrentDictionary(); private ConcurrentDictionary KeyboardHooks = new ConcurrentDictionary(); + private ConcurrentDictionary MouseHooks = new ConcurrentDictionary(); /// /// 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 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 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); - } } } diff --git a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj index f7476236..900b809a 100644 --- a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj +++ b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj @@ -60,6 +60,7 @@ + @@ -71,6 +72,8 @@ + + diff --git a/SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCT.cs b/SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCT.cs index 06fb3cc2..ad66e4d0 100644 --- a/SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCT.cs +++ b/SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCT.cs @@ -15,17 +15,17 @@ namespace SafeExamBrowser.WindowsApi.Types /// See http://www.pinvoke.net/default.aspx/Structures/KBDLLHOOKSTRUCT.html. /// [StructLayout(LayoutKind.Sequential)] - public struct KBDLLHOOKSTRUCT + internal struct KBDLLHOOKSTRUCT { /// /// A virtual-key code. The code must be a value in the range 1 to 254. /// - public uint KeyCode; + internal uint KeyCode; /// /// A hardware scan code for the key. /// - public uint ScanCode; + internal uint ScanCode; /// /// 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. /// - public KBDLLHOOKSTRUCTFlags Flags; + internal KBDLLHOOKSTRUCTFlags Flags; /// /// The time stamp for this message, equivalent to what GetMessageTime would return for this message. /// - public uint Time; + internal uint Time; /// /// Additional information associated with the message. /// - public IntPtr DwExtraInfo; + internal IntPtr DwExtraInfo; } } diff --git a/SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCTFlags.cs b/SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCTFlags.cs index 0e38ac8f..1b2342fe 100644 --- a/SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCTFlags.cs +++ b/SafeExamBrowser.WindowsApi/Types/KBDLLHOOKSTRUCTFlags.cs @@ -11,7 +11,7 @@ namespace SafeExamBrowser.WindowsApi.Types /// /// See http://www.pinvoke.net/default.aspx/Structures/KBDLLHOOKSTRUCT.html. /// - public enum KBDLLHOOKSTRUCTFlags + internal enum KBDLLHOOKSTRUCTFlags { /// /// Test the extended-key flag. diff --git a/SafeExamBrowser.WindowsApi/Types/MSLLHOOKSTRUCT.cs b/SafeExamBrowser.WindowsApi/Types/MSLLHOOKSTRUCT.cs new file mode 100644 index 00000000..c3cf36b6 --- /dev/null +++ b/SafeExamBrowser.WindowsApi/Types/MSLLHOOKSTRUCT.cs @@ -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; + } +} diff --git a/SafeExamBrowser.WindowsApi/Types/POINT.cs b/SafeExamBrowser.WindowsApi/Types/POINT.cs new file mode 100644 index 00000000..ceaf74f1 --- /dev/null +++ b/SafeExamBrowser.WindowsApi/Types/POINT.cs @@ -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; + } +} diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs index 78c5d2a6..a844b607 100644 --- a/SafeExamBrowser/CompositionRoot.cs +++ b/SafeExamBrowser/CompositionRoot.cs @@ -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(); - 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));