SEBWIN-312: Finished first draft of task view.

This commit is contained in:
dbuechel 2019-11-20 10:45:08 +01:00
parent 08bf49b61b
commit fbe03b86ea
20 changed files with 227 additions and 85 deletions

View file

@ -21,13 +21,12 @@ namespace SafeExamBrowser.Applications
private const int ONE_SECOND = 1000;
private ILogger logger;
private string name;
private IProcess process;
private Timer timer;
public IconResource Icon { get; }
public InstanceIdentifier Id { get; }
public string Name { get; }
public string Name { get; private set; }
public event IconChangedEventHandler IconChanged { add { } remove { } }
public event NameChangedEventHandler NameChanged;
@ -114,12 +113,12 @@ namespace SafeExamBrowser.Applications
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
var success = process.TryGetWindowTitle(out var title);
var hasChanged = name?.Equals(title, StringComparison.Ordinal) != true;
var hasChanged = Name?.Equals(title, StringComparison.Ordinal) != true;
if (success && hasChanged)
{
name = title;
NameChanged?.Invoke(name);
Name = title;
NameChanged?.Invoke(Name);
}
timer.Start();

View file

@ -194,8 +194,9 @@ namespace SafeExamBrowser.Browser
private void Control_TitleChanged(string title)
{
window.UpdateTitle(title);
NameChanged?.Invoke(title);
Name = title;
window.UpdateTitle(Name);
NameChanged?.Invoke(Name);
}
private void DisplayHandler_FaviconChanged(string uri)

View file

@ -24,6 +24,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
private ClientContext context;
private Mock<ILogger> logger;
private Mock<ITaskbar> taskbar;
private Mock<ITaskView> taskView;
private Mock<IUserInterfaceFactory> uiFactory;
private BrowserOperation sut;
@ -36,11 +37,12 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
context = new ClientContext();
logger = new Mock<ILogger>();
taskbar = new Mock<ITaskbar>();
taskView = new Mock<ITaskView>();
uiFactory = new Mock<IUserInterfaceFactory>();
context.Browser = browser.Object;
sut = new BrowserOperation(actionCenter.Object, context, logger.Object, taskbar.Object, uiFactory.Object);
sut = new BrowserOperation(actionCenter.Object, context, logger.Object, taskbar.Object, taskView.Object, uiFactory.Object);
}
[TestMethod]
@ -59,5 +61,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
sut.Revert();
browser.Verify(c => c.Terminate(), Times.Once);
}
[TestMethod]
public void TODO()
{
// TODO: Test initialization of task view!
}
}
}

View file

@ -71,6 +71,7 @@ namespace SafeExamBrowser.Client
private IRuntimeProxy runtimeProxy;
private ISystemInfo systemInfo;
private ITaskbar taskbar;
private ITaskView taskView;
private IText text;
private ITextResource textResource;
private IUserInterfaceFactory uiFactory;
@ -94,6 +95,7 @@ namespace SafeExamBrowser.Client
uiFactory = BuildUserInterfaceFactory();
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client);
taskbar = BuildTaskbar();
taskView = BuildTaskView();
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
var applicationFactory = new ApplicationFactory(ModuleLogger(nameof(ApplicationFactory)), processFactory);
@ -197,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, uiFactory);
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskView, uiFactory);
context.Browser = browser;
@ -242,7 +244,6 @@ namespace SafeExamBrowser.Client
var logInfo = new LogNotificationInfo(text);
var logController = new LogNotificationController(logger, uiFactory);
var powerSupply = new PowerSupply(ModuleLogger(nameof(PowerSupply)));
var taskView = BuildTaskView();
var wirelessAdapter = new WirelessAdapter(ModuleLogger(nameof(WirelessAdapter)));
var operation = new ShellOperation(
actionCenter,

View file

@ -20,6 +20,7 @@ namespace SafeExamBrowser.Client.Operations
private IActionCenter actionCenter;
private ILogger logger;
private ITaskbar taskbar;
private ITaskView taskView;
private IUserInterfaceFactory uiFactory;
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
@ -30,11 +31,13 @@ namespace SafeExamBrowser.Client.Operations
ClientContext context,
ILogger logger,
ITaskbar taskbar,
ITaskView taskView,
IUserInterfaceFactory uiFactory) : base(context)
{
this.actionCenter = actionCenter;
this.logger = logger;
this.taskbar = taskbar;
this.taskView = taskView;
this.uiFactory = uiFactory;
}
@ -55,6 +58,8 @@ namespace SafeExamBrowser.Client.Operations
taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.Taskbar), true);
}
taskView.Add(Context.Browser);
return OperationResult.Success;
}

View file

@ -4,10 +4,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="#EEF0F0F0" AllowsTransparency="True" WindowStyle="None" Topmost="True" ResizeMode="NoResize">
mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="{DynamicResource BackgroundTransparentBrush}"
AllowsTransparency="True" WindowStyle="None" Topmost="True" ResizeMode="NoResize">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

View file

@ -20,8 +20,8 @@
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ApplicationName" Background="#99D3D3D3" FontWeight="Bold" Padding="5" TextAlignment="Center" />
<TextBlock Grid.Row="0" x:Name="ApplicationName" Background="{StaticResource BackgroundTransparentEmphasisBrush}" FontWeight="Bold" Padding="5" TextAlignment="Center" />
<ContentControl Grid.Row="1" x:Name="ApplicationButton" FontWeight="Bold" />
<StackPanel Grid.Row="2" x:Name="InstancePanel" Background="#99D3D3D3" Orientation="Vertical" />
<StackPanel Grid.Row="2" x:Name="InstancePanel" Background="{StaticResource BackgroundTransparentEmphasisBrush}" Orientation="Vertical" />
</Grid>
</UserControl>

View file

@ -0,0 +1,21 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.TaskViewInstanceControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="5" Height="150" Width="250">
<Border Name="Border" Background="{StaticResource BackgroundTransparentEmphasisBrush}" BorderBrush="DarkGray" BorderThickness="2" Visibility="Hidden" />
<StackPanel HorizontalAlignment="Center" Orientation="Vertical" VerticalAlignment="Center">
<ContentControl Name="Icon" MaxWidth="40" />
<TextBlock Name="Title" Margin="10" Foreground="{StaticResource PrimaryTextBrush}" TextTrimming="CharacterEllipsis" />
</StackPanel>
</Grid>
</UserControl>

View file

@ -0,0 +1,69 @@
/*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class TaskViewInstanceControl : UserControl
{
private IApplicationInstance instance;
internal InstanceIdentifier Id => instance.Id;
public TaskViewInstanceControl(IApplicationInstance instance)
{
this.instance = instance;
InitializeComponent();
InitializeControl();
}
internal void Activate()
{
instance.Activate();
}
internal void Deselect()
{
Border.Visibility = Visibility.Hidden;
Icon.MaxWidth = 40;
Title.FontWeight = FontWeights.Normal;
}
internal void Select()
{
Border.Visibility = Visibility.Visible;
Icon.MaxWidth = 50;
Title.FontWeight = FontWeights.SemiBold;
}
private void InitializeControl()
{
Icon.Content = IconResourceLoader.Load(instance.Icon);
Title.Text = instance.Name;
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
}
private void Instance_NameChanged(string name)
{
Dispatcher.InvokeAsync(() => Title.Text = name);
}
private void Instance_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(instance.Icon));
}
}
}

View file

@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Foreground="White" Height="500" Width="1100" MinHeight="350"
mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Foreground="White" Height="750" Width="1000" MinHeight="350"
MinWidth="350" WindowStartupLocation="CenterScreen" Icon="./Images/LogNotification.ico">
<Window.Resources>
<ResourceDictionary>

View file

@ -142,6 +142,9 @@
<Compile Include="Controls\TaskbarWirelessNetworkControl.xaml.cs">
<DependentUpon>TaskbarWirelessNetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskViewInstanceControl.xaml.cs">
<DependentUpon>TaskViewInstanceControl.xaml</DependentUpon>
</Compile>
<Compile Include="LockScreen.xaml.cs">
<DependentUpon>LockScreen.xaml</DependentUpon>
</Compile>
@ -314,6 +317,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Controls\TaskViewInstanceControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="LockScreen.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View file

@ -4,8 +4,15 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
mc:Ignorable="d" AllowsTransparency="True" Background="#AA000000" BorderBrush="DodgerBlue" BorderThickness="1" Title="TaskView"
Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None">
mc:Ignorable="d" AllowsTransparency="True" Background="{DynamicResource BackgroundTransparentBrush}" BorderBrush="DodgerBlue" BorderThickness="1"
Title="TaskView" Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<StackPanel Name="Rows" Margin="10" Orientation="Vertical" />
</Grid>

View file

@ -11,23 +11,24 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
using SafeExamBrowser.UserInterface.Desktop.Controls;
namespace SafeExamBrowser.UserInterface.Desktop
{
public partial class TaskView : Window, ITaskView
{
private IList<IApplicationInstance> instances;
private LinkedListNode<TaskViewInstanceControl> current;
private LinkedList<TaskViewInstanceControl> controls;
private List<IApplicationInstance> instances;
public TaskView()
{
InitializeComponent();
controls = new LinkedList<TaskViewInstanceControl>();
instances = new List<IApplicationInstance>();
InitializeComponent();
}
public void Add(IApplication application)
@ -44,61 +45,84 @@ namespace SafeExamBrowser.UserInterface.Desktop
private void Application_InstanceStarted(IApplicationInstance instance)
{
Dispatcher.InvokeAsync(() =>
{
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
instance.Terminated += Instance_Terminated;
instances.Add(instance);
Update();
});
Dispatcher.InvokeAsync(() => Add(instance));
}
private void Activator_Deactivated()
{
Dispatcher.InvokeAsync(Hide);
Dispatcher.InvokeAsync(ActivateAndHide);
}
private void Activator_Next()
{
Dispatcher.InvokeAsync(ShowConditional);
Dispatcher.InvokeAsync(SelectNext);
}
private void Activator_Previous()
{
Dispatcher.InvokeAsync(ShowConditional);
}
private void Instance_IconChanged(IconResource icon)
{
// TODO Dispatcher.InvokeAsync(...);
}
private void Instance_NameChanged(string name)
{
// TODO Dispatcher.InvokeAsync(...);
Dispatcher.InvokeAsync(SelectPrevious);
}
private void Instance_Terminated(InstanceIdentifier id)
{
Dispatcher.InvokeAsync(() =>
{
var instance = instances.FirstOrDefault(i => i.Id == id);
Dispatcher.InvokeAsync(() => Remove(id));
}
if (instance != default(IApplicationInstance))
{
instances.Remove(instance);
Update();
}
});
private void ActivateAndHide()
{
Activate();
current?.Value.Activate();
Hide();
}
private void Add(IApplicationInstance instance)
{
instance.Terminated += Instance_Terminated;
instances.Add(instance);
Update();
}
private void Remove(InstanceIdentifier id)
{
var instance = instances.FirstOrDefault(i => i.Id == id);
if (instance != default(IApplicationInstance))
{
instances.Remove(instance);
Update();
}
}
private void SelectNext()
{
ShowConditional();
if (current != null)
{
current.Value.Deselect();
current = current.Next ?? controls.First;
current.Value.Select();
}
}
private void SelectPrevious()
{
ShowConditional();
if (current != null)
{
current.Value.Deselect();
current = current.Previous ?? controls.Last;
current.Value.Select();
}
}
private void ShowConditional()
{
if (Visibility != Visibility.Visible && instances.Any())
if (instances.Any() && Visibility != Visibility.Visible)
{
Show();
Activate();
}
}
@ -107,6 +131,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
var max = Math.Ceiling(Math.Sqrt(instances.Count));
var stack = new Stack<IApplicationInstance>(instances);
controls.Clear();
Rows.Children.Clear();
for (var rowCount = 0; rowCount < max && stack.Any(); rowCount++)
@ -118,12 +143,16 @@ namespace SafeExamBrowser.UserInterface.Desktop
for (var columnIndex = 0; columnIndex < max && stack.Any(); columnIndex++)
{
var instance = stack.Pop();
var control = BuildInstanceControl(instance);
var control = new TaskViewInstanceControl(instance);
controls.AddLast(control);
row.Children.Add(control);
}
}
current = controls.First;
current?.Value.Select();
UpdateLayout();
Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
@ -134,28 +163,5 @@ namespace SafeExamBrowser.UserInterface.Desktop
Hide();
}
}
private UIElement BuildInstanceControl(IApplicationInstance instance)
{
var border = new Border();
var stackPanel = new StackPanel();
var icon = IconResourceLoader.Load(instance.Icon);
border.BorderBrush = Brushes.White;
border.BorderThickness = new Thickness(1);
border.Height = 150;
border.Margin = new Thickness(5);
border.Padding = new Thickness(2);
border.Width = 250;
border.Child = stackPanel;
stackPanel.HorizontalAlignment = HorizontalAlignment.Center;
stackPanel.Orientation = Orientation.Vertical;
stackPanel.VerticalAlignment = VerticalAlignment.Center;
stackPanel.Children.Add(new ContentControl { Content = icon, MaxWidth = 50 });
stackPanel.Children.Add(new TextBlock(new Run($"Instance {instance.Name ?? "NULL"}") { Foreground = Brushes.White, FontWeight = FontWeights.Bold }));
return border;
}
}
}

View file

@ -5,10 +5,12 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" Title="Taskbar" Background="#FFF0F0F0" Height="40" Width="750" WindowStyle="None" Topmost="True" ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
mc:Ignorable="d" Title="Taskbar" Background="{DynamicResource BackgroundBrush}" Height="40" Width="750" WindowStyle="None" Topmost="True"
ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
@ -26,7 +28,7 @@
</ScrollViewer>
<StackPanel Grid.Column="1" x:Name="NotificationStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
<StackPanel Grid.Column="2" x:Name="SystemControlStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
<local:TaskbarClock Grid.Column="3" x:Name="Clock" Foreground="DimGray" Padding="10,0,10,0" />
<local:TaskbarClock Grid.Column="3" x:Name="Clock" Foreground="{StaticResource SecondaryTextBrush}" Padding="10,0,10,0" />
<local:TaskbarQuitButton Grid.Column="4" x:Name="QuitButton" />
</Grid>
</Window>

View file

@ -1,5 +1,9 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="BackgroundBrush">#FFF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="ActionCenterDarkBrush">#AA808080</SolidColorBrush>
<SolidColorBrush x:Key="BackgroundBrush">#FFF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="BackgroundTransparentBrush">#EEF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="BackgroundTransparentEmphasisBrush">#99D3D3D3</SolidColorBrush>
<SolidColorBrush x:Key="PrimaryTextBrush">Black</SolidColorBrush>
<SolidColorBrush x:Key="SecondaryTextBrush">DimGray</SolidColorBrush>
</ResourceDictionary>

View file

@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile"
mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Foreground="White" Height="500" Width="1100" MinHeight="350"
mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Foreground="White" Height="750" Width="1000" MinHeight="350"
MinWidth="350" WindowState="Maximized" WindowStartupLocation="CenterScreen" Icon="./Images/LogNotification.ico">
<Window.Resources>
<ResourceDictionary>

View file

@ -342,7 +342,7 @@ namespace SafeExamBrowser.WindowsApi
public void RestoreWindow(IntPtr window)
{
User32.ShowWindow(window, (int)ShowWindowCommand.Restore);
User32.ShowWindow(window, (int) ShowWindowCommand.Restore);
}
public bool ResumeThread(int threadId)

View file

@ -69,15 +69,18 @@ namespace SafeExamBrowser.WindowsApi
{
try
{
var success = User32.BringWindowToTop(process.MainWindowHandle);
var success = true;
var placement = new WINDOWPLACEMENT();
success &= User32.BringWindowToTop(process.MainWindowHandle);
success &= User32.SetForegroundWindow(process.MainWindowHandle);
placement.length = Marshal.SizeOf(placement);
User32.GetWindowPlacement(process.MainWindowHandle, ref placement);
if (placement.showCmd == (int) ShowWindowCommand.ShowMinimized)
{
success &= User32.ShowWindow(process.MainWindowHandle, (int) ShowWindowCommand.Restore);
success &= User32.ShowWindowAsync(process.MainWindowHandle, (int) ShowWindowCommand.Restore);
}
return success;

View file

@ -59,6 +59,7 @@ namespace SafeExamBrowser.WindowsApi
logger.Info($"Attempting to start process '{path}'...");
// TODO: Fails for certain processes, whereas Process.Start() does not -> use different API?! Use (declare?) CreateProcessW / CreateProcessA?
var success = Kernel32.CreateProcess(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, Constant.NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref startupInfo, ref processInfo);
if (!success)

View file

@ -83,6 +83,9 @@ namespace SafeExamBrowser.WindowsApi
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, EventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
@ -92,6 +95,9 @@ namespace SafeExamBrowser.WindowsApi
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SwitchDesktop(IntPtr hDesktop);