SEBWIN-312: Implemented basic handling of whitelisted applications.
This commit is contained in:
parent
3d55bd6ff4
commit
35dd3dd4c2
19 changed files with 313 additions and 33 deletions
|
@ -30,19 +30,29 @@ namespace SafeExamBrowser.Applications.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event IconChangedEventHandler IconChanged;
|
event IconChangedEventHandler IconChanged;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event fired when the application instance has been terminated.
|
|
||||||
/// </summary>
|
|
||||||
event InstanceTerminatedEventHandler Terminated;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event fired when the name or (document) title of the application instance has changed.
|
/// Event fired when the name or (document) title of the application instance has changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event NameChangedEventHandler NameChanged;
|
event NameChangedEventHandler NameChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event fired when the application instance has been terminated.
|
||||||
|
/// </summary>
|
||||||
|
event InstanceTerminatedEventHandler Terminated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Makes this instance the currently active one and brings it to the foreground.
|
/// Makes this instance the currently active one and brings it to the foreground.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Activate();
|
void Activate();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the application instance.
|
||||||
|
/// </summary>
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Terminates the application instance.
|
||||||
|
/// </summary>
|
||||||
|
void Terminate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,19 @@ using SafeExamBrowser.Applications.Contracts;
|
||||||
using SafeExamBrowser.Core.Contracts;
|
using SafeExamBrowser.Core.Contracts;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.Settings.Applications;
|
using SafeExamBrowser.Settings.Applications;
|
||||||
|
using SafeExamBrowser.WindowsApi.Contracts;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Applications
|
namespace SafeExamBrowser.Applications
|
||||||
{
|
{
|
||||||
public class ApplicationFactory : IApplicationFactory
|
public class ApplicationFactory : IApplicationFactory
|
||||||
{
|
{
|
||||||
private ILogger logger;
|
private IModuleLogger logger;
|
||||||
|
private IProcessFactory processFactory;
|
||||||
|
|
||||||
public ApplicationFactory(ILogger logger)
|
public ApplicationFactory(IModuleLogger logger, IProcessFactory processFactory)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
this.processFactory = processFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FactoryResult TryCreate(WhitelistApplication settings, out IApplication application)
|
public FactoryResult TryCreate(WhitelistApplication settings, out IApplication application)
|
||||||
|
@ -62,7 +65,7 @@ namespace SafeExamBrowser.Applications
|
||||||
{
|
{
|
||||||
var icon = new IconResource { Type = IconResourceType.Embedded, Uri = new Uri(executablePath) };
|
var icon = new IconResource { Type = IconResourceType.Embedded, Uri = new Uri(executablePath) };
|
||||||
var info = new ApplicationInfo { IconResource = icon, Name = settings.DisplayName, Tooltip = settings.Description ?? settings.DisplayName };
|
var info = new ApplicationInfo { IconResource = icon, Name = settings.DisplayName, Tooltip = settings.Description ?? settings.DisplayName };
|
||||||
var application = new ExternalApplication(executablePath, info);
|
var application = new ExternalApplication(executablePath, info, logger.CloneFor(settings.DisplayName), processFactory);
|
||||||
|
|
||||||
return application;
|
return application;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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 SafeExamBrowser.Applications.Contracts;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Applications
|
||||||
|
{
|
||||||
|
internal class ApplicationInstanceIdentifier : InstanceIdentifier
|
||||||
|
{
|
||||||
|
internal int ProcessId { get; private set; }
|
||||||
|
|
||||||
|
public ApplicationInstanceIdentifier(int processId)
|
||||||
|
{
|
||||||
|
ProcessId = processId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object other)
|
||||||
|
{
|
||||||
|
if (other is ApplicationInstanceIdentifier id)
|
||||||
|
{
|
||||||
|
return ProcessId == id.ProcessId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return ProcessId.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"({ProcessId})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,38 +6,64 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using SafeExamBrowser.Applications.Contracts;
|
using SafeExamBrowser.Applications.Contracts;
|
||||||
using SafeExamBrowser.Applications.Contracts.Events;
|
using SafeExamBrowser.Applications.Contracts.Events;
|
||||||
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
using SafeExamBrowser.WindowsApi.Contracts;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Applications
|
namespace SafeExamBrowser.Applications
|
||||||
{
|
{
|
||||||
internal class ExternalApplication : IApplication
|
internal class ExternalApplication : IApplication
|
||||||
{
|
{
|
||||||
private string executablePath;
|
private string executablePath;
|
||||||
|
private IModuleLogger logger;
|
||||||
|
private IList<IApplicationInstance> instances;
|
||||||
|
private IProcessFactory processFactory;
|
||||||
|
|
||||||
public ApplicationInfo Info { get; }
|
public ApplicationInfo Info { get; }
|
||||||
|
|
||||||
public event InstanceStartedEventHandler InstanceStarted;
|
public event InstanceStartedEventHandler InstanceStarted;
|
||||||
|
|
||||||
internal ExternalApplication(string executablePath, ApplicationInfo info)
|
internal ExternalApplication(string executablePath, ApplicationInfo info, IModuleLogger logger, IProcessFactory processFactory)
|
||||||
{
|
{
|
||||||
this.executablePath = executablePath;
|
this.executablePath = executablePath;
|
||||||
this.Info = info;
|
this.Info = info;
|
||||||
|
this.logger = logger;
|
||||||
|
this.instances = new List<IApplicationInstance>();
|
||||||
|
this.processFactory = processFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
|
// Nothing to do here for now.
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
|
logger.Info("Starting application...");
|
||||||
|
|
||||||
|
var process = processFactory.StartNew(executablePath);
|
||||||
|
var id = new ApplicationInstanceIdentifier(process.Id);
|
||||||
|
var instance = new ExternalApplicationInstance(id, logger.CloneFor($"{Info.Name} {id}"), process);
|
||||||
|
|
||||||
|
instance.Initialize();
|
||||||
|
instances.Add(instance);
|
||||||
|
InstanceStarted?.Invoke(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Terminate()
|
public void Terminate()
|
||||||
{
|
{
|
||||||
|
if (instances.Any())
|
||||||
|
{
|
||||||
|
logger.Info("Terminating application...");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var instance in instances)
|
||||||
|
{
|
||||||
|
instance.Terminate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
115
SafeExamBrowser.Applications/ExternalApplicationInstance.cs
Normal file
115
SafeExamBrowser.Applications/ExternalApplicationInstance.cs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* 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.Timers;
|
||||||
|
using SafeExamBrowser.Applications.Contracts;
|
||||||
|
using SafeExamBrowser.Applications.Contracts.Events;
|
||||||
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
using SafeExamBrowser.WindowsApi.Contracts;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Applications
|
||||||
|
{
|
||||||
|
internal class ExternalApplicationInstance : IApplicationInstance
|
||||||
|
{
|
||||||
|
private const int ONE_SECOND = 1000;
|
||||||
|
|
||||||
|
private ILogger logger;
|
||||||
|
private string name;
|
||||||
|
private IProcess process;
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
public InstanceIdentifier Id { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public event IconChangedEventHandler IconChanged { add { } remove { } }
|
||||||
|
public event NameChangedEventHandler NameChanged;
|
||||||
|
public event InstanceTerminatedEventHandler Terminated;
|
||||||
|
|
||||||
|
public ExternalApplicationInstance(InstanceIdentifier id, ILogger logger, IProcess process)
|
||||||
|
{
|
||||||
|
this.Id = id;
|
||||||
|
this.logger = logger;
|
||||||
|
this.process = process;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Activate()
|
||||||
|
{
|
||||||
|
var success = process.TryActivate();
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
logger.Warn("Failed to activate instance!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
process.Terminated += Process_Terminated;
|
||||||
|
|
||||||
|
timer = new Timer(ONE_SECOND);
|
||||||
|
timer.Elapsed += Timer_Elapsed;
|
||||||
|
timer.Start();
|
||||||
|
|
||||||
|
logger.Info("Initialized application instance.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Terminate()
|
||||||
|
{
|
||||||
|
const int MAX_ATTEMPTS = 5;
|
||||||
|
const int TIMEOUT_MS = 500;
|
||||||
|
|
||||||
|
timer.Elapsed -= Timer_Elapsed;
|
||||||
|
timer?.Stop();
|
||||||
|
|
||||||
|
var terminated = process.HasTerminated;
|
||||||
|
|
||||||
|
for (var attempt = 0; attempt < MAX_ATTEMPTS && !terminated; attempt++)
|
||||||
|
{
|
||||||
|
terminated = process.TryClose(TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var attempt = 0; attempt < MAX_ATTEMPTS && !terminated; attempt++)
|
||||||
|
{
|
||||||
|
terminated = process.TryKill(TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (terminated)
|
||||||
|
{
|
||||||
|
logger.Info("Successfully terminated application instance.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Failed to terminate application instance!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Process_Terminated(int exitCode)
|
||||||
|
{
|
||||||
|
logger.Info($"Application instance has terminated with exit code {exitCode}.");
|
||||||
|
Terminated?.Invoke(Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
var success = process.TryGetWindowTitle(out var title);
|
||||||
|
var hasChanged = name?.Equals(title, StringComparison.Ordinal) != true;
|
||||||
|
|
||||||
|
// TODO: Use this and ensure log window doesn't hang on shutdown (if it is open)!
|
||||||
|
// logger.Warn($"Success: {success} HasChanged: {hasChanged}");
|
||||||
|
|
||||||
|
if (success && hasChanged)
|
||||||
|
{
|
||||||
|
name = title;
|
||||||
|
NameChanged?.Invoke(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,7 +55,9 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ApplicationFactory.cs" />
|
<Compile Include="ApplicationFactory.cs" />
|
||||||
|
<Compile Include="ApplicationInstanceIdentifier.cs" />
|
||||||
<Compile Include="ExternalApplication.cs" />
|
<Compile Include="ExternalApplication.cs" />
|
||||||
|
<Compile Include="ExternalApplicationInstance.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -75,6 +77,10 @@
|
||||||
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
|
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
|
||||||
<Name>SafeExamBrowser.Settings</Name>
|
<Name>SafeExamBrowser.Settings</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\SafeExamBrowser.WindowsApi.Contracts\SafeExamBrowser.WindowsApi.Contracts.csproj">
|
||||||
|
<Project>{7016f080-9aa5-41b2-a225-385ad877c171}</Project>
|
||||||
|
<Name>SafeExamBrowser.WindowsApi.Contracts</Name>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
|
@ -53,9 +53,9 @@ namespace SafeExamBrowser.Browser
|
||||||
|
|
||||||
public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
||||||
public event IconChangedEventHandler IconChanged;
|
public event IconChangedEventHandler IconChanged;
|
||||||
public event InstanceTerminatedEventHandler Terminated;
|
|
||||||
public event NameChangedEventHandler NameChanged;
|
public event NameChangedEventHandler NameChanged;
|
||||||
public event PopupRequestedEventHandler PopupRequested;
|
public event PopupRequestedEventHandler PopupRequested;
|
||||||
|
public event InstanceTerminatedEventHandler Terminated;
|
||||||
|
|
||||||
public BrowserApplicationInstance(
|
public BrowserApplicationInstance(
|
||||||
AppConfig appConfig,
|
AppConfig appConfig,
|
||||||
|
@ -85,13 +85,13 @@ namespace SafeExamBrowser.Browser
|
||||||
window?.BringToForeground();
|
window?.BringToForeground();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
InitializeControl();
|
InitializeControl();
|
||||||
InitializeWindow();
|
InitializeWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Terminate()
|
public void Terminate()
|
||||||
{
|
{
|
||||||
window?.Close();
|
window?.Close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace SafeExamBrowser.Browser
|
||||||
{
|
{
|
||||||
internal class BrowserInstanceIdentifier : InstanceIdentifier
|
internal class BrowserInstanceIdentifier : InstanceIdentifier
|
||||||
{
|
{
|
||||||
public int Value { get; private set; }
|
internal int Value { get; private set; }
|
||||||
|
|
||||||
public BrowserInstanceIdentifier(int id)
|
public BrowserInstanceIdentifier(int id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -96,7 +96,8 @@ namespace SafeExamBrowser.Client
|
||||||
taskbar = BuildTaskbar();
|
taskbar = BuildTaskbar();
|
||||||
terminationActivator = new TerminationActivator(ModuleLogger(nameof(TerminationActivator)));
|
terminationActivator = new TerminationActivator(ModuleLogger(nameof(TerminationActivator)));
|
||||||
|
|
||||||
var applicationFactory = new ApplicationFactory(ModuleLogger(nameof(ApplicationFactory)));
|
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
|
||||||
|
var applicationFactory = new ApplicationFactory(ModuleLogger(nameof(ApplicationFactory)), processFactory);
|
||||||
var applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, new ProcessFactory(ModuleLogger(nameof(ProcessFactory))));
|
var applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, new ProcessFactory(ModuleLogger(nameof(ProcessFactory))));
|
||||||
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
||||||
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</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="#99D3D3D3" FontWeight="Bold" Padding="5" TextAlignment="Center" />
|
||||||
<ContentControl Grid.Row="1" x:Name="ApplicationButton" />
|
<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="#99D3D3D3" Orientation="Vertical" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -23,6 +23,6 @@
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
</Popup>
|
</Popup>
|
||||||
<Button x:Name="Button" Background="{StaticResource BackgroundBrush}" Click="Button_Click" Padding="4" Template="{StaticResource TaskbarButton}" Width="50" />
|
<Button x:Name="Button" Background="{StaticResource BackgroundBrush}" Padding="4" Template="{StaticResource TaskbarButton}" Width="50" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
</Grid.RowDefinitions>
|
</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="#99D3D3D3" FontWeight="Bold" Padding="5" TextAlignment="Center" />
|
||||||
<ContentControl Grid.Row="1" x:Name="ApplicationButton" />
|
<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="#99D3D3D3" Orientation="Vertical" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -23,6 +23,6 @@
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
</Popup>
|
</Popup>
|
||||||
<Button x:Name="Button" Background="{StaticResource BackgroundBrush}" Click="Button_Click" Padding="4" Template="{StaticResource TaskbarButton}" Width="60" />
|
<Button x:Name="Button" Background="{StaticResource BackgroundBrush}" Padding="4" Template="{StaticResource TaskbarButton}" Width="60" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
@ -40,6 +40,12 @@ namespace SafeExamBrowser.WindowsApi.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event ProcessTerminatedEventHandler Terminated;
|
event ProcessTerminatedEventHandler Terminated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to activate the process (i.e. bring its main window to the foreground). This may only work for interactive processes which have
|
||||||
|
/// a main window. Returns <c>true</c> if the process was successfully activated, otherwise <c>false</c>.
|
||||||
|
/// </summary>
|
||||||
|
bool TryActivate();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to gracefully terminate the process by closing its main window. This will only work for interactive processes which have a main
|
/// Attempts to gracefully terminate the process by closing its main window. This will only work for interactive processes which have a main
|
||||||
/// window. Optionally waits the specified amount of time for the process to terminate. Returns <c>true</c> if the process has terminated,
|
/// window. Optionally waits the specified amount of time for the process to terminate. Returns <c>true</c> if the process has terminated,
|
||||||
|
@ -47,6 +53,12 @@ namespace SafeExamBrowser.WindowsApi.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool TryClose(int timeout_ms = 0);
|
bool TryClose(int timeout_ms = 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to retrieve the title of the main window of the process. This will only work if for interactive processes which have a main
|
||||||
|
/// window. Returns <c>true</c> if the title was successfully retrieved, otherwise <c>false</c>.
|
||||||
|
/// </summary>
|
||||||
|
bool TryGetWindowTitle(out string title);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to immediately kill the process. Optionally waits the specified amount of time for the process to terminate. Returns <c>true</c>
|
/// Attempts to immediately kill the process. Optionally waits the specified amount of time for the process to terminate. Returns <c>true</c>
|
||||||
/// if the process has terminated, otherwise <c>false</c>.
|
/// if the process has terminated, otherwise <c>false</c>.
|
||||||
|
|
|
@ -19,7 +19,6 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
internal class Kernel32
|
internal class Kernel32
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool CloseHandle(IntPtr hObject);
|
internal static extern bool CloseHandle(IntPtr hObject);
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
|
|
@ -11,9 +11,12 @@ using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
using SafeExamBrowser.WindowsApi.Constants;
|
||||||
using SafeExamBrowser.WindowsApi.Contracts;
|
using SafeExamBrowser.WindowsApi.Contracts;
|
||||||
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
||||||
|
using SafeExamBrowser.WindowsApi.Types;
|
||||||
|
|
||||||
namespace SafeExamBrowser.WindowsApi
|
namespace SafeExamBrowser.WindowsApi
|
||||||
{
|
{
|
||||||
|
@ -62,6 +65,31 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
this.originalNameInitialized = true;
|
this.originalNameInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryActivate()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var success = User32.BringWindowToTop(process.MainWindowHandle);
|
||||||
|
var placement = new WINDOWPLACEMENT();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to activate process!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryClose(int timeout_ms = 0)
|
public bool TryClose(int timeout_ms = 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -90,6 +118,25 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetWindowTitle(out string title)
|
||||||
|
{
|
||||||
|
title = default(string);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
process.Refresh();
|
||||||
|
title = process.MainWindowTitle;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to retrieve title of main window!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryKill(int timeout_ms = 0)
|
public bool TryKill(int timeout_ms = 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Management" />
|
<Reference Include="System.Management" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -91,6 +92,7 @@
|
||||||
<Compile Include="Types\RECT.cs" />
|
<Compile Include="Types\RECT.cs" />
|
||||||
<Compile Include="Types\STARTUPINFO.cs" />
|
<Compile Include="Types\STARTUPINFO.cs" />
|
||||||
<Compile Include="Types\Window.cs" />
|
<Compile Include="Types\Window.cs" />
|
||||||
|
<Compile Include="Types\WINDOWPLACEMENT.cs" />
|
||||||
<Compile Include="User32.cs" />
|
<Compile Include="User32.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
22
SafeExamBrowser.WindowsApi/Types/WINDOWPLACEMENT.cs
Normal file
22
SafeExamBrowser.WindowsApi/Types/WINDOWPLACEMENT.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.Drawing;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.WindowsApi.Types
|
||||||
|
{
|
||||||
|
internal struct WINDOWPLACEMENT
|
||||||
|
{
|
||||||
|
public int length;
|
||||||
|
public int flags;
|
||||||
|
public int showCmd;
|
||||||
|
public Point ptMinPosition;
|
||||||
|
public Point ptMaxPosition;
|
||||||
|
public Rectangle rcNormalPosition;
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,6 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
internal static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
internal static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool CloseClipboard();
|
internal static extern bool CloseClipboard();
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
@ -34,15 +33,15 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
internal static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags, uint dwDesiredAccess, IntPtr lpsa);
|
internal static extern IntPtr CreateDesktop(string lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags, uint dwDesiredAccess, IntPtr lpsa);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
internal static extern bool BringWindowToTop(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
internal static extern bool EmptyClipboard();
|
internal static extern bool EmptyClipboard();
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopDelegate lpEnumFunc, IntPtr lParam);
|
internal static extern bool EnumDesktops(IntPtr hwinsta, EnumDesktopDelegate lpEnumFunc, IntPtr lParam);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool EnumWindows(EnumWindowsDelegate enumProc, IntPtr lParam);
|
internal static extern bool EnumWindows(EnumWindowsDelegate enumProc, IntPtr lParam);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
@ -60,6 +59,9 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
internal static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, IntPtr pvInfo, int nLength, ref int lpnLengthNeeded);
|
internal static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, IntPtr pvInfo, int nLength, ref int lpnLengthNeeded);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
internal static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
|
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
|
||||||
|
|
||||||
|
@ -70,15 +72,12 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool IsWindowVisible(IntPtr hWnd);
|
internal static extern bool IsWindowVisible(IntPtr hWnd);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool OpenClipboard(IntPtr hWndNewOwner);
|
internal static extern bool OpenClipboard(IntPtr hWndNewOwner);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
internal static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
@ -91,25 +90,21 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
internal static extern IntPtr SetWindowsHookEx(HookType hookType, HookDelegate lpfn, IntPtr hMod, uint dwThreadId);
|
internal static extern IntPtr SetWindowsHookEx(HookType hookType, HookDelegate lpfn, IntPtr hMod, uint dwThreadId);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
internal static extern bool SwitchDesktop(IntPtr hDesktop);
|
internal static extern bool SwitchDesktop(IntPtr hDesktop);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref RECT pvParam, SPIF fWinIni);
|
internal static extern bool SystemParametersInfo(SPI uiAction, uint uiParam, ref RECT pvParam, SPIF fWinIni);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool SystemParametersInfo(SPI uiAction, int uiParam, string pvParam, SPIF fWinIni);
|
internal static extern bool SystemParametersInfo(SPI uiAction, int uiParam, string pvParam, SPIF fWinIni);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
|
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
internal static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
internal static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue