diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs index 50547810..00e9bcd0 100644 --- a/SafeExamBrowser.Contracts/I18n/TextKey.cs +++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs @@ -23,6 +23,7 @@ namespace SafeExamBrowser.Contracts.I18n MessageBox_StartupErrorTitle, Notification_AboutTooltip, Notification_LogTooltip, + SplashScreen_EmptyClipboard, SplashScreen_InitializeBrowser, SplashScreen_InitializeProcessMonitoring, SplashScreen_InitializeTaskbar, diff --git a/SafeExamBrowser.Contracts/WindowsApi/INativeMethods.cs b/SafeExamBrowser.Contracts/WindowsApi/INativeMethods.cs index 2cf66181..a3c88c6a 100644 --- a/SafeExamBrowser.Contracts/WindowsApi/INativeMethods.cs +++ b/SafeExamBrowser.Contracts/WindowsApi/INativeMethods.cs @@ -38,6 +38,14 @@ namespace SafeExamBrowser.Contracts.WindowsApi /// void DeregisterSystemEvent(IntPtr handle); + /// + /// Empties the clipboard. + /// + /// + /// If the emptying of the clipboard failed. + /// + void EmptyClipboard(); + /// /// Retrieves a collection of handles to all currently open (i.e. visible) windows. /// diff --git a/SafeExamBrowser.Core/Behaviour/Operations/ClipboardOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/ClipboardOperation.cs new file mode 100644 index 00000000..5403223f --- /dev/null +++ b/SafeExamBrowser.Core/Behaviour/Operations/ClipboardOperation.cs @@ -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 SafeExamBrowser.Contracts.Behaviour; +using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Contracts.UserInterface; +using SafeExamBrowser.Contracts.WindowsApi; + +namespace SafeExamBrowser.Core.Behaviour.Operations +{ + public class ClipboardOperation : IOperation + { + private ILogger logger; + private INativeMethods nativeMethods; + + public ISplashScreen SplashScreen { private get; set; } + + public ClipboardOperation(ILogger logger, INativeMethods nativeMethods) + { + this.logger = logger; + this.nativeMethods = nativeMethods; + } + + public void Perform() + { + EmptyClipboard(); + } + + public void Revert() + { + EmptyClipboard(); + } + + private void EmptyClipboard() + { + logger.Info("Emptying clipboard..."); + SplashScreen.UpdateText(TextKey.SplashScreen_EmptyClipboard); + + nativeMethods.EmptyClipboard(); + } + } +} diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml index cad11af8..73fd5e5a 100644 --- a/SafeExamBrowser.Core/I18n/Text.xml +++ b/SafeExamBrowser.Core/I18n/Text.xml @@ -8,6 +8,7 @@ Startup Error About Safe Exam Browser Application Log + Emptying clipboard Initializing browser Initializing process monitoring Initializing taskbar diff --git a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj index 7cd14296..1bb728e2 100644 --- a/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj +++ b/SafeExamBrowser.Core/SafeExamBrowser.Core.csproj @@ -58,6 +58,7 @@ + diff --git a/SafeExamBrowser.UserInterface/Utilities/IconResourceLoader.cs b/SafeExamBrowser.UserInterface/Utilities/IconResourceLoader.cs index cba02954..d38c5dee 100644 --- a/SafeExamBrowser.UserInterface/Utilities/IconResourceLoader.cs +++ b/SafeExamBrowser.UserInterface/Utilities/IconResourceLoader.cs @@ -9,7 +9,9 @@ using System; using System.Windows; using System.Windows.Controls; +using System.Windows.Documents; using System.Windows.Markup; +using System.Windows.Media; using System.Windows.Media.Imaging; using SafeExamBrowser.Contracts.Configuration; @@ -19,22 +21,39 @@ namespace SafeExamBrowser.UserInterface.Utilities { internal static UIElement Load(IIconResource resource) { - if (resource.IsBitmapResource) + try { - return new Image + if (resource.IsBitmapResource) { - Source = new BitmapImage(resource.Uri) - }; - } - else if (resource.IsXamlResource) - { - using (var stream = Application.GetResourceStream(resource.Uri)?.Stream) - { - return XamlReader.Load(stream) as UIElement; + return LoadBitmapResource(resource); } + else if (resource.IsXamlResource) + { + return LoadXamlResource(resource); + } + } + catch (Exception) + { + return new TextBlock(new Run("X") { Foreground = Brushes.Red, FontWeight = FontWeights.Bold }); } throw new NotSupportedException($"Application icon resource of type '{resource.GetType()}' is not supported!"); } + + private static UIElement LoadBitmapResource(IIconResource resource) + { + return new Image + { + Source = new BitmapImage(resource.Uri) + }; + } + + private static UIElement LoadXamlResource(IIconResource resource) + { + using (var stream = Application.GetResourceStream(resource.Uri)?.Stream) + { + return XamlReader.Load(stream) as UIElement; + } + } } } diff --git a/SafeExamBrowser.WindowsApi/NativeMethods.cs b/SafeExamBrowser.WindowsApi/NativeMethods.cs index aac9aff1..fbaf5d19 100644 --- a/SafeExamBrowser.WindowsApi/NativeMethods.cs +++ b/SafeExamBrowser.WindowsApi/NativeMethods.cs @@ -94,6 +94,20 @@ namespace SafeExamBrowser.WindowsApi EventDelegates.TryRemove(handle, out EventProc d); } + public void EmptyClipboard() + { + var success = true; + + success &= User32.OpenClipboard(IntPtr.Zero); + success &= User32.EmptyClipboard(); + success &= User32.CloseClipboard(); + + if (!success) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + } + public IEnumerable GetOpenWindows() { var windows = new List(); diff --git a/SafeExamBrowser.WindowsApi/User32.cs b/SafeExamBrowser.WindowsApi/User32.cs index 0d6f5ed2..f888d4f3 100644 --- a/SafeExamBrowser.WindowsApi/User32.cs +++ b/SafeExamBrowser.WindowsApi/User32.cs @@ -26,6 +26,14 @@ namespace SafeExamBrowser.WindowsApi [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 CloseClipboard(); + + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool EmptyClipboard(); + [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool EnumWindows(EnumWindowsDelegate enumProc, IntPtr lParam); @@ -46,6 +54,10 @@ namespace SafeExamBrowser.WindowsApi [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool IsWindowVisible(IntPtr hWnd); + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool OpenClipboard(IntPtr hWndNewOwner); + [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs index 33eeba07..5403d6b9 100644 --- a/SafeExamBrowser/CompositionRoot.cs +++ b/SafeExamBrowser/CompositionRoot.cs @@ -86,6 +86,7 @@ namespace SafeExamBrowser StartupOperations.Enqueue(new TaskbarOperation(logger, logFormatter, settings, Taskbar, text, uiFactory)); StartupOperations.Enqueue(new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory)); StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger)); + StartupOperations.Enqueue(new ClipboardOperation(logger, nativeMethods)); StartupOperations.Enqueue(new MouseInterceptorOperation(logger, mouseInterceptor, nativeMethods)); } }