diff --git a/SafeExamBrowser.Applications.Contracts/IApplicationWindow.cs b/SafeExamBrowser.Applications.Contracts/IApplicationWindow.cs
index 5ac1deb7..818c7ad6 100644
--- a/SafeExamBrowser.Applications.Contracts/IApplicationWindow.cs
+++ b/SafeExamBrowser.Applications.Contracts/IApplicationWindow.cs
@@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
using SafeExamBrowser.Applications.Contracts.Events;
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
@@ -16,6 +17,11 @@ namespace SafeExamBrowser.Applications.Contracts
///
public interface IApplicationWindow
{
+ ///
+ /// The native handle of the window.
+ ///
+ IntPtr Handle { get; }
+
///
/// The icon of the window.
///
diff --git a/SafeExamBrowser.Applications/ExternalApplicationWindow.cs b/SafeExamBrowser.Applications/ExternalApplicationWindow.cs
index 13489c98..6d6c5456 100644
--- a/SafeExamBrowser.Applications/ExternalApplicationWindow.cs
+++ b/SafeExamBrowser.Applications/ExternalApplicationWindow.cs
@@ -18,7 +18,7 @@ namespace SafeExamBrowser.Applications
{
private INativeMethods nativeMethods;
- internal IntPtr Handle { get; }
+ public IntPtr Handle { get; }
public IconResource Icon { get; private set; }
public string Title { get; private set; }
diff --git a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
index 64aa7b90..0e7fdee5 100644
--- a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
+++ b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
@@ -51,6 +51,7 @@ namespace SafeExamBrowser.Browser
internal int Id { get; }
+ public IntPtr Handle { get; private set; }
public IconResource Icon { get; private set; }
public string Title { get; private set; }
@@ -178,6 +179,8 @@ namespace SafeExamBrowser.Browser
window.UpdateZoomLevel(CalculateZoomPercentage());
window.Show();
+ Handle = window.Handle;
+
logger.Debug("Initialized browser window.");
}
diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs
index 52bc5453..51056978 100644
--- a/SafeExamBrowser.Client/CompositionRoot.cs
+++ b/SafeExamBrowser.Client/CompositionRoot.cs
@@ -71,7 +71,7 @@ namespace SafeExamBrowser.Client
private IRuntimeProxy runtimeProxy;
private ISystemInfo systemInfo;
private ITaskbar taskbar;
- private ITaskView taskView;
+ private ITaskView taskview;
private IText text;
private ITextResource textResource;
private IUserInterfaceFactory uiFactory;
@@ -95,7 +95,7 @@ namespace SafeExamBrowser.Client
uiFactory = BuildUserInterfaceFactory();
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client);
taskbar = BuildTaskbar();
- taskView = BuildTaskView();
+ taskview = BuildTaskView();
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
var applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, processFactory);
@@ -199,7 +199,7 @@ namespace SafeExamBrowser.Client
{
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
- var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskView, uiFactory);
+ var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskview, uiFactory);
context.Browser = browser;
@@ -258,7 +258,7 @@ namespace SafeExamBrowser.Client
powerSupply,
systemInfo,
taskbar,
- taskView,
+ taskview,
text,
uiFactory,
wirelessAdapter);
@@ -311,7 +311,7 @@ namespace SafeExamBrowser.Client
case UserInterfaceMode.Mobile:
return new Mobile.TaskView();
default:
- return new Desktop.TaskView();
+ return new Desktop.Taskview();
}
}
diff --git a/SafeExamBrowser.Client/Operations/BrowserOperation.cs b/SafeExamBrowser.Client/Operations/BrowserOperation.cs
index eeda4df5..f62e85ff 100644
--- a/SafeExamBrowser.Client/Operations/BrowserOperation.cs
+++ b/SafeExamBrowser.Client/Operations/BrowserOperation.cs
@@ -20,7 +20,7 @@ namespace SafeExamBrowser.Client.Operations
private IActionCenter actionCenter;
private ILogger logger;
private ITaskbar taskbar;
- private ITaskView taskView;
+ private ITaskView taskview;
private IUserInterfaceFactory uiFactory;
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
@@ -31,13 +31,13 @@ namespace SafeExamBrowser.Client.Operations
ClientContext context,
ILogger logger,
ITaskbar taskbar,
- ITaskView taskView,
+ ITaskView taskview,
IUserInterfaceFactory uiFactory) : base(context)
{
this.actionCenter = actionCenter;
this.logger = logger;
this.taskbar = taskbar;
- this.taskView = taskView;
+ this.taskview = taskview;
this.uiFactory = uiFactory;
}
@@ -58,7 +58,7 @@ namespace SafeExamBrowser.Client.Operations
taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.Taskbar), true);
}
- taskView.Add(Context.Browser);
+ taskview.Add(Context.Browser);
return OperationResult.Success;
}
diff --git a/SafeExamBrowser.Client/Operations/ShellOperation.cs b/SafeExamBrowser.Client/Operations/ShellOperation.cs
index 7ba8cae1..4577ef45 100644
--- a/SafeExamBrowser.Client/Operations/ShellOperation.cs
+++ b/SafeExamBrowser.Client/Operations/ShellOperation.cs
@@ -35,7 +35,7 @@ namespace SafeExamBrowser.Client.Operations
private IPowerSupply powerSupply;
private ISystemInfo systemInfo;
private ITaskbar taskbar;
- private ITaskView taskView;
+ private ITaskView taskview;
private IText text;
private IUserInterfaceFactory uiFactory;
private IWirelessAdapter wirelessAdapter;
@@ -56,7 +56,7 @@ namespace SafeExamBrowser.Client.Operations
IPowerSupply powerSupply,
ISystemInfo systemInfo,
ITaskbar taskbar,
- ITaskView taskView,
+ ITaskView taskview,
IText text,
IUserInterfaceFactory uiFactory,
IWirelessAdapter wirelessAdapter) : base(context)
@@ -73,7 +73,7 @@ namespace SafeExamBrowser.Client.Operations
this.systemInfo = systemInfo;
this.text = text;
this.taskbar = taskbar;
- this.taskView = taskView;
+ this.taskview = taskview;
this.uiFactory = uiFactory;
this.wirelessAdapter = wirelessAdapter;
}
@@ -116,7 +116,7 @@ namespace SafeExamBrowser.Client.Operations
if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskViewActivator taskViewActivator)
{
- taskView.Register(taskViewActivator);
+ taskview.Register(taskViewActivator);
taskViewActivator.Start();
}
@@ -177,7 +177,7 @@ namespace SafeExamBrowser.Client.Operations
foreach (var application in Context.Applications)
{
- taskView.Add(application);
+ taskview.Add(application);
}
}
diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
index 858ab9f0..d51bc5c2 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
@@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
using SafeExamBrowser.UserInterface.Contracts.Windows;
@@ -26,6 +27,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
/// Enables the forward navigation button.
///
bool CanNavigateForwards { set; }
+
+ ///
+ /// The native handle of the window.
+ ///
+ IntPtr Handle { get; }
///
/// Event fired when the user changed the URL.
diff --git a/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs
index 537a9e6e..210f12f0 100644
--- a/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
+using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
@@ -40,6 +41,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
public bool CanNavigateBackwards { set => Dispatcher.Invoke(() => BackwardButton.IsEnabled = value); }
public bool CanNavigateForwards { set => Dispatcher.Invoke(() => ForwardButton.IsEnabled = value); }
+ public IntPtr Handle { get; private set; }
public event AddressChangedEventHandler AddressChanged;
public event ActionRequestedEventHandler BackwardNavigationRequested;
@@ -157,6 +159,8 @@ namespace SafeExamBrowser.UserInterface.Desktop
private void BrowserWindow_Loaded(object sender, RoutedEventArgs e)
{
+ Handle = new WindowInteropHelper(this).Handle;
+
if (isMainWindow)
{
WindowUtility.DisableCloseButtonFor(this);
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewWindowControl.xaml b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewWindowControl.xaml
index 1c5353f3..4a71b36c 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewWindowControl.xaml
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewWindowControl.xaml
@@ -1,4 +1,4 @@
-
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewWindowControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewWindowControl.xaml.cs
index 34ad9fee..d99409ea 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewWindowControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewWindowControl.xaml.cs
@@ -6,21 +6,26 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Threading;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
- public partial class TaskViewWindowControl : UserControl
+ public partial class TaskviewWindowControl : UserControl
{
+ private Taskview taskview;
+ private IntPtr thumbnail;
private IApplicationWindow window;
- public TaskViewWindowControl(IApplicationWindow window)
+ public TaskviewWindowControl(IApplicationWindow window, Taskview taskview)
{
this.window = window;
+ this.taskview = taskview;
InitializeComponent();
InitializeControl();
@@ -33,35 +38,128 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
internal void Deselect()
{
- Icon.MaxWidth = 40;
Indicator.Visibility = Visibility.Hidden;
Title.FontWeight = FontWeights.Normal;
}
+ internal void Destroy()
+ {
+ if (thumbnail != IntPtr.Zero)
+ {
+ Thumbnail.DwmUnregisterThumbnail(thumbnail);
+ }
+ }
+
internal void Select()
{
- Icon.MaxWidth = 50;
Indicator.Visibility = Visibility.Visible;
Title.FontWeight = FontWeights.SemiBold;
}
+ internal void Update()
+ {
+ if (!IsLoaded || !IsVisible)
+ {
+ return;
+ }
+
+ if (thumbnail == IntPtr.Zero && taskview.Handle != IntPtr.Zero && window.Handle != IntPtr.Zero)
+ {
+ Thumbnail.DwmRegisterThumbnail(taskview.Handle, window.Handle, out thumbnail);
+ }
+
+ if (thumbnail != IntPtr.Zero)
+ {
+ Thumbnail.DwmQueryThumbnailSourceSize(thumbnail, out var size);
+
+ var destination = CalculatePhysicalDestination(size);
+ var properties = new Thumbnail.Properties
+ {
+ Destination = destination,
+ Flags = Thumbnail.DWM_TNP_RECTDESTINATION | Thumbnail.DWM_TNP_VISIBLE,
+ Visible = true
+ };
+
+ Thumbnail.DwmUpdateThumbnailProperties(thumbnail, ref properties);
+ }
+ }
+
+ private void TaskViewWindowControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.NewValue as bool? == true)
+ {
+ Update();
+ }
+ }
+
+ private void Window_TitleChanged(string title)
+ {
+ Dispatcher.InvokeAsync(() => Title.Text = title);
+ }
+
+ private void Window_IconChanged(IconResource icon)
+ {
+ Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(window.Icon));
+ }
+
+ private Thumbnail.Rectangle CalculatePhysicalDestination(Thumbnail.Size size)
+ {
+ var controlToTaskview = TransformToVisual(taskview);
+ var placeholderToControl = Placeholder.TransformToVisual(this);
+ var placeholderLeft = placeholderToControl.Transform(new Point(0, 0)).X;
+ var placeholderTop = placeholderToControl.Transform(new Point(0, 0)).Y;
+ var placeholderRight = placeholderToControl.Transform(new Point(Placeholder.ActualWidth, 0)).X;
+ var placeholderBottom = placeholderToControl.Transform(new Point(0, Placeholder.ActualHeight)).Y;
+
+ var physicalBounds = new Thumbnail.Rectangle
+ {
+ Left = (int) Math.Round(this.TransformToPhysical(controlToTaskview.Transform(new Point(placeholderLeft, 0)).X, 0).X),
+ Top = (int) Math.Round(this.TransformToPhysical(0, controlToTaskview.Transform(new Point(0, placeholderTop)).Y).Y),
+ Right = (int) Math.Round(this.TransformToPhysical(controlToTaskview.Transform(new Point(placeholderRight, 0)).X, 0).X),
+ Bottom = (int) Math.Round(this.TransformToPhysical(0, controlToTaskview.Transform(new Point(0, placeholderBottom)).Y).Y)
+ };
+
+ var scaleFactor = default(double);
+ var thumbnailHeight = default(double);
+ var thumbnailWidth = default(double);
+ var maxWidth = (double) physicalBounds.Right - physicalBounds.Left;
+ var maxHeight = (double) physicalBounds.Bottom - physicalBounds.Top;
+ var placeholderRatio = maxWidth / maxHeight;
+ var windowRatio = (double) size.X / size.Y;
+
+ if (windowRatio < placeholderRatio)
+ {
+ thumbnailHeight = maxHeight;
+ scaleFactor = thumbnailHeight / size.Y;
+ thumbnailWidth = size.X * scaleFactor;
+ }
+ else
+ {
+ thumbnailWidth = maxWidth;
+ scaleFactor = thumbnailWidth / size.X;
+ thumbnailHeight = size.Y * scaleFactor;
+ }
+
+ var widthDifference = maxWidth - thumbnailWidth;
+ var heightDifference = maxHeight - thumbnailHeight;
+
+ return new Thumbnail.Rectangle
+ {
+ Left = (int) Math.Round(physicalBounds.Left + (widthDifference / 2)),
+ Top = (int) Math.Round(physicalBounds.Top + (heightDifference / 2)),
+ Right = (int) Math.Round(physicalBounds.Right - (widthDifference / 2)),
+ Bottom = (int) Math.Round(physicalBounds.Bottom - (heightDifference / 2))
+ };
+ }
+
private void InitializeControl()
{
Icon.Content = IconResourceLoader.Load(window.Icon);
+ IsVisibleChanged += TaskViewWindowControl_IsVisibleChanged;
+ Loaded += (o, args) => Update();
Title.Text = window.Title;
-
- window.IconChanged += Instance_IconChanged;
- window.TitleChanged += Instance_TitleChanged;
- }
-
- private void Instance_TitleChanged(string title)
- {
- Dispatcher.InvokeAsync(() => Title.Text = title);
- }
-
- private void Instance_IconChanged(IconResource icon)
- {
- Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(window.Icon));
+ window.IconChanged += Window_IconChanged;
+ window.TitleChanged += Window_TitleChanged;
}
}
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj
index f5b2bb3d..d86976d3 100644
--- a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj
+++ b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj
@@ -142,8 +142,8 @@
TaskbarWirelessNetworkControl.xaml
-
- TaskViewWindowControl.xaml
+
+ TaskviewWindowControl.xaml
LockScreen.xaml
@@ -161,8 +161,8 @@
SplashScreen.xaml
-
- TaskView.xaml
+
+ Taskview.xaml
@@ -317,7 +317,7 @@
MSBuild:Compile
Designer
-
+
Designer
MSBuild:Compile
@@ -325,7 +325,7 @@
Designer
MSBuild:Compile
-
+
Designer
MSBuild:Compile
diff --git a/SafeExamBrowser.UserInterface.Desktop/TaskView.xaml b/SafeExamBrowser.UserInterface.Desktop/TaskView.xaml
index 3103e6c8..1a61fe1f 100644
--- a/SafeExamBrowser.UserInterface.Desktop/TaskView.xaml
+++ b/SafeExamBrowser.UserInterface.Desktop/TaskView.xaml
@@ -1,11 +1,11 @@
-
+ Title="Taskview" Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None">
diff --git a/SafeExamBrowser.UserInterface.Desktop/TaskView.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/TaskView.xaml.cs
index 35479681..1bea115c 100644
--- a/SafeExamBrowser.UserInterface.Desktop/TaskView.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/TaskView.xaml.cs
@@ -11,24 +11,28 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Interop;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Desktop.Controls;
namespace SafeExamBrowser.UserInterface.Desktop
{
- public partial class TaskView : Window, ITaskView
+ public partial class Taskview : Window, ITaskView
{
private IList applications;
- private LinkedListNode current;
- private LinkedList controls;
+ private LinkedListNode current;
+ private LinkedList controls;
- public TaskView()
+ internal IntPtr Handle { get; private set; }
+
+ public Taskview()
{
applications = new List();
- controls = new LinkedList();
+ controls = new LinkedList();
InitializeComponent();
+ InitializeTaskview();
}
public void Add(IApplication application)
@@ -66,9 +70,21 @@ namespace SafeExamBrowser.UserInterface.Desktop
private void ActivateAndHide()
{
- Activate();
- current?.Value.Activate();
- Hide();
+ if (IsVisible)
+ {
+ Activate();
+ current?.Value.Activate();
+ Hide();
+ }
+ }
+
+ private void InitializeTaskview()
+ {
+ Loaded += (o, args) =>
+ {
+ Handle = new WindowInteropHelper(this).Handle;
+ Update();
+ };
}
private void SelectNext()
@@ -106,31 +122,37 @@ namespace SafeExamBrowser.UserInterface.Desktop
private void Update()
{
- var windows = new Stack();
+ ClearTaskview();
+ LoadControls();
+ UpdateLocation();
+ }
- foreach (var application in applications)
+ private void ClearTaskview()
+ {
+ foreach (var control in controls)
{
- foreach (var window in application.GetWindows())
- {
- windows.Push(window);
- }
+ control.Destroy();
}
- var max = Math.Ceiling(Math.Sqrt(windows.Count));
-
controls.Clear();
Rows.Children.Clear();
+ }
- for (var rowCount = 0; rowCount < max && windows.Any(); rowCount++)
+ private void LoadControls()
+ {
+ var windows = GetAllWindows();
+ var maxColumns = Math.Ceiling(Math.Sqrt(windows.Count));
+
+ while (windows.Any())
{
var row = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center };
Rows.Children.Add(row);
- for (var columnIndex = 0; columnIndex < max && windows.Any(); columnIndex++)
+ for (var column = 0; column < maxColumns && windows.Any(); column++)
{
var window = windows.Pop();
- var control = new TaskViewWindowControl(window);
+ var control = new TaskviewWindowControl(window, this);
controls.AddLast(control);
row.Children.Add(control);
@@ -139,16 +161,36 @@ namespace SafeExamBrowser.UserInterface.Desktop
current = controls.First;
current?.Value.Select();
+ }
- UpdateLayout();
+ private void UpdateLocation()
+ {
+ if (controls.Any())
+ {
+ UpdateLayout();
- Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
- Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top;
-
- if (!controls.Any())
+ Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
+ Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top;
+ }
+ else
{
Hide();
}
}
+
+ private Stack GetAllWindows()
+ {
+ var stack = new Stack();
+
+ foreach (var application in applications)
+ {
+ foreach (var window in application.GetWindows())
+ {
+ stack.Push(window);
+ }
+ }
+
+ return stack;
+ }
}
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/BrowserWindow.xaml.cs
index f8c282ed..90c3220c 100644
--- a/SafeExamBrowser.UserInterface.Mobile/BrowserWindow.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/BrowserWindow.xaml.cs
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
+using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
@@ -40,6 +41,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
public bool CanNavigateBackwards { set => Dispatcher.Invoke(() => BackwardButton.IsEnabled = value); }
public bool CanNavigateForwards { set => Dispatcher.Invoke(() => ForwardButton.IsEnabled = value); }
+ public IntPtr Handle { get; private set; }
public event AddressChangedEventHandler AddressChanged;
public event ActionRequestedEventHandler BackwardNavigationRequested;
@@ -157,6 +159,8 @@ namespace SafeExamBrowser.UserInterface.Mobile
private void BrowserWindow_Loaded(object sender, RoutedEventArgs e)
{
+ Handle = new WindowInteropHelper(this).Handle;
+
if (isMainWindow)
{
WindowUtility.DisableCloseButtonFor(this);
diff --git a/SafeExamBrowser.UserInterface.Shared/SafeExamBrowser.UserInterface.Shared.csproj b/SafeExamBrowser.UserInterface.Shared/SafeExamBrowser.UserInterface.Shared.csproj
index 92e0291e..4f3471b5 100644
--- a/SafeExamBrowser.UserInterface.Shared/SafeExamBrowser.UserInterface.Shared.csproj
+++ b/SafeExamBrowser.UserInterface.Shared/SafeExamBrowser.UserInterface.Shared.csproj
@@ -73,6 +73,7 @@
Code
+
diff --git a/SafeExamBrowser.UserInterface.Shared/Utilities/Thumbnail.cs b/SafeExamBrowser.UserInterface.Shared/Utilities/Thumbnail.cs
new file mode 100644
index 00000000..ac10f139
--- /dev/null
+++ b/SafeExamBrowser.UserInterface.Shared/Utilities/Thumbnail.cs
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace SafeExamBrowser.UserInterface.Shared.Utilities
+{
+ ///
+ /// See https://docs.microsoft.com/en-us/windows/win32/dwm/thumbnail-ovw.
+ ///
+ public static class Thumbnail
+ {
+ public const int DWM_TNP_VISIBLE = 0x8;
+ public const int DWM_TNP_OPACITY = 0x4;
+ public const int DWM_TNP_RECTDESTINATION = 0x1;
+ public const int S_OK = 0;
+
+ [DllImport("dwmapi.dll")]
+ public static extern int DwmQueryThumbnailSourceSize(IntPtr thumbnail, out Size size);
+
+ [DllImport("dwmapi.dll")]
+ public static extern int DwmRegisterThumbnail(IntPtr destinationWindow, IntPtr sourceWindow, out IntPtr thumbnail);
+
+ [DllImport("dwmapi.dll")]
+ public static extern int DwmUnregisterThumbnail(IntPtr thumbnail);
+
+ [DllImport("dwmapi.dll")]
+ public static extern int DwmUpdateThumbnailProperties(IntPtr thumbnail, ref Properties properties);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Properties
+ {
+ public int Flags;
+ public Rectangle Destination;
+ public Rectangle Source;
+ public byte Opacity;
+ public bool Visible;
+ public bool SourceClientAreaOnly;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Size
+ {
+ public int X;
+ public int Y;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Rectangle
+ {
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
+ }
+ }
+}
diff --git a/SafeExamBrowser.WindowsApi/ProcessFactory.cs b/SafeExamBrowser.WindowsApi/ProcessFactory.cs
index 238bbc10..4a7cd6fc 100644
--- a/SafeExamBrowser.WindowsApi/ProcessFactory.cs
+++ b/SafeExamBrowser.WindowsApi/ProcessFactory.cs
@@ -72,29 +72,6 @@ namespace SafeExamBrowser.WindowsApi
return process;
}
- private System.Diagnostics.Process StartOnDesktop(string path, params string[] args)
- {
- var commandLine = $"{'"' + path + '"'} {string.Join(" ", args)}";
- var processInfo = new PROCESS_INFORMATION();
- var startupInfo = new STARTUPINFO();
-
- startupInfo.cb = Marshal.SizeOf(startupInfo);
- startupInfo.lpDesktop = StartupDesktop?.Name;
-
- var success = Kernel32.CreateProcess(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, Constant.NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref startupInfo, ref processInfo);
-
- if (success)
- {
- return System.Diagnostics.Process.GetProcessById(processInfo.dwProcessId);
- }
-
- var errorCode = Marshal.GetLastWin32Error();
-
- logger.Error($"Failed to start process '{path}' on desktop '{StartupDesktop}'! Error code: {errorCode}.");
-
- throw new Win32Exception(errorCode);
- }
-
public bool TryGetById(int id, out IProcess process)
{
var raw = System.Diagnostics.Process.GetProcesses().FirstOrDefault(p => p.Id == id);
@@ -183,5 +160,28 @@ namespace SafeExamBrowser.WindowsApi
{
return logger.CloneFor($"{nameof(Process)} '{name}' ({process.Id})");
}
+
+ private System.Diagnostics.Process StartOnDesktop(string path, params string[] args)
+ {
+ var commandLine = $"{'"' + path + '"'} {string.Join(" ", args)}";
+ var processInfo = new PROCESS_INFORMATION();
+ var startupInfo = new STARTUPINFO();
+
+ startupInfo.cb = Marshal.SizeOf(startupInfo);
+ startupInfo.lpDesktop = StartupDesktop?.Name;
+
+ var success = Kernel32.CreateProcess(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, Constant.NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref startupInfo, ref processInfo);
+
+ if (success)
+ {
+ return System.Diagnostics.Process.GetProcessById(processInfo.dwProcessId);
+ }
+
+ var errorCode = Marshal.GetLastWin32Error();
+
+ logger.Error($"Failed to start process '{path}' on desktop '{StartupDesktop}'! Error code: {errorCode}.");
+
+ throw new Win32Exception(errorCode);
+ }
}
}