SEBWIN-312: Implemented basic window handling for external applications. Reverted wrong cleanup logic for native handles.
This commit is contained in:
parent
4503cbe778
commit
f19f284d95
20 changed files with 243 additions and 221 deletions
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Applications.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an identifier which uniquely identifies an instance in the context of an application.
|
||||
/// </summary>
|
||||
public abstract class InstanceIdentifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether two identifiers are equal (i.e. whether they identify the same application instance).
|
||||
/// </summary>
|
||||
public static bool operator ==(InstanceIdentifier a, InstanceIdentifier b) => Equals(a, b);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two identifiers are different (i.e. whether they identify different application instances).
|
||||
/// </summary>
|
||||
public static bool operator !=(InstanceIdentifier a, InstanceIdentifier b) => !Equals(a, b);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the given object is an identifier for the same application instance.
|
||||
/// </summary>
|
||||
public abstract override bool Equals(object other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for the identifier.
|
||||
/// </summary>
|
||||
public abstract override int GetHashCode();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a human-readable string representation of the identifier.
|
||||
/// </summary>
|
||||
public abstract override string ToString();
|
||||
}
|
||||
}
|
|
@ -62,7 +62,6 @@
|
|||
<Compile Include="IApplicationFactory.cs" />
|
||||
<Compile Include="ApplicationInfo.cs" />
|
||||
<Compile Include="IApplicationWindow.cs" />
|
||||
<Compile Include="InstanceIdentifier.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -21,11 +21,13 @@ namespace SafeExamBrowser.Applications
|
|||
public class ApplicationFactory : IApplicationFactory
|
||||
{
|
||||
private IModuleLogger logger;
|
||||
private INativeMethods nativeMethods;
|
||||
private IProcessFactory processFactory;
|
||||
|
||||
public ApplicationFactory(IModuleLogger logger, IProcessFactory processFactory)
|
||||
public ApplicationFactory(IModuleLogger logger, INativeMethods nativeMethods, IProcessFactory processFactory)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.processFactory = processFactory;
|
||||
}
|
||||
|
||||
|
@ -65,7 +67,7 @@ namespace SafeExamBrowser.Applications
|
|||
{
|
||||
var icon = new IconResource { Type = IconResourceType.Embedded, Uri = new Uri(executablePath) };
|
||||
var info = new ApplicationInfo { AutoStart = settings.AutoStart, Icon = icon, Name = settings.DisplayName, Tooltip = settings.Description ?? settings.DisplayName };
|
||||
var application = new ExternalApplication(executablePath, info, logger.CloneFor(settings.DisplayName), processFactory);
|
||||
var application = new ExternalApplication(executablePath, info, logger.CloneFor(settings.DisplayName), nativeMethods, processFactory);
|
||||
|
||||
return application;
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* 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})";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Applications.Events
|
||||
{
|
||||
internal delegate void InstanceTerminatedEventHandler(int id);
|
||||
}
|
|
@ -18,8 +18,11 @@ namespace SafeExamBrowser.Applications
|
|||
{
|
||||
internal class ExternalApplication : IApplication
|
||||
{
|
||||
private int instanceIdCounter = default(int);
|
||||
|
||||
private string executablePath;
|
||||
private IModuleLogger logger;
|
||||
private INativeMethods nativeMethods;
|
||||
private IList<ExternalApplicationInstance> instances;
|
||||
private IProcessFactory processFactory;
|
||||
|
||||
|
@ -27,18 +30,24 @@ namespace SafeExamBrowser.Applications
|
|||
|
||||
public ApplicationInfo Info { get; }
|
||||
|
||||
internal ExternalApplication(string executablePath, ApplicationInfo info, IModuleLogger logger, IProcessFactory processFactory)
|
||||
internal ExternalApplication(
|
||||
string executablePath,
|
||||
ApplicationInfo info,
|
||||
IModuleLogger logger,
|
||||
INativeMethods nativeMethods,
|
||||
IProcessFactory processFactory)
|
||||
{
|
||||
this.executablePath = executablePath;
|
||||
this.Info = info;
|
||||
this.logger = logger;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.instances = new List<ExternalApplicationInstance>();
|
||||
this.processFactory = processFactory;
|
||||
}
|
||||
|
||||
public IEnumerable<IApplicationWindow> GetWindows()
|
||||
{
|
||||
return Enumerable.Empty<IApplicationWindow>();
|
||||
return instances.SelectMany(i => i.GetWindows());
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
|
@ -53,10 +62,13 @@ namespace SafeExamBrowser.Applications
|
|||
logger.Info("Starting application...");
|
||||
|
||||
var process = processFactory.StartNew(executablePath);
|
||||
var id = new ApplicationInstanceIdentifier(process.Id);
|
||||
var instance = new ExternalApplicationInstance(Info.Icon, id, logger.CloneFor($"{Info.Name} {id}"), process);
|
||||
var id = ++instanceIdCounter;
|
||||
var instanceLogger = logger.CloneFor($"{Info.Name} Instance #{id}");
|
||||
var instance = new ExternalApplicationInstance(Info.Icon, id, instanceLogger, nativeMethods, process);
|
||||
|
||||
instance.Initialize();
|
||||
instance.Terminated += Instance_Terminated;
|
||||
instance.WindowsChanged += () => WindowsChanged?.Invoke();
|
||||
instances.Add(instance);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -65,6 +77,12 @@ namespace SafeExamBrowser.Applications
|
|||
}
|
||||
}
|
||||
|
||||
private void Instance_Terminated(int id)
|
||||
{
|
||||
instances.Remove(instances.First(i => i.Id == id));
|
||||
WindowsChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
if (instances.Any())
|
||||
|
|
|
@ -6,7 +6,13 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Timers;
|
||||
using SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Applications.Contracts.Events;
|
||||
using SafeExamBrowser.Applications.Events;
|
||||
using SafeExamBrowser.Core.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
@ -15,22 +21,42 @@ namespace SafeExamBrowser.Applications
|
|||
{
|
||||
internal class ExternalApplicationInstance
|
||||
{
|
||||
private IconResource icon;
|
||||
private InstanceIdentifier id;
|
||||
private ILogger logger;
|
||||
private IProcess process;
|
||||
private readonly object @lock = new object();
|
||||
|
||||
internal ExternalApplicationInstance(IconResource icon, InstanceIdentifier id, ILogger logger, IProcess process)
|
||||
private IconResource icon;
|
||||
private ILogger logger;
|
||||
private INativeMethods nativeMethods;
|
||||
private IProcess process;
|
||||
private Timer timer;
|
||||
private IList<ExternalApplicationWindow> windows;
|
||||
|
||||
internal int Id { get; }
|
||||
|
||||
internal event InstanceTerminatedEventHandler Terminated;
|
||||
internal event WindowsChangedEventHandler WindowsChanged;
|
||||
|
||||
internal ExternalApplicationInstance(IconResource icon, int id, ILogger logger, INativeMethods nativeMethods, IProcess process)
|
||||
{
|
||||
this.icon = icon;
|
||||
this.id = id;
|
||||
this.Id = id;
|
||||
this.logger = logger;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.process = process;
|
||||
this.windows = new List<ExternalApplicationWindow>();
|
||||
}
|
||||
|
||||
internal IEnumerable<IApplicationWindow> GetWindows()
|
||||
{
|
||||
lock (@lock)
|
||||
{
|
||||
return new List<IApplicationWindow>(windows);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Initialize()
|
||||
{
|
||||
process.Terminated += Process_Terminated;
|
||||
InitializeEvents();
|
||||
logger.Info("Initialized application instance.");
|
||||
}
|
||||
|
||||
internal void Terminate()
|
||||
|
@ -42,7 +68,7 @@ namespace SafeExamBrowser.Applications
|
|||
|
||||
if (!terminated)
|
||||
{
|
||||
process.Terminated -= Process_Terminated;
|
||||
FinalizeEvents();
|
||||
|
||||
for (var attempt = 0; attempt < MAX_ATTEMPTS && !terminated; attempt++)
|
||||
{
|
||||
|
@ -68,7 +94,69 @@ namespace SafeExamBrowser.Applications
|
|||
private void Process_Terminated(int exitCode)
|
||||
{
|
||||
logger.Info($"Application instance has terminated with exit code {exitCode}.");
|
||||
// TODO: Terminated?.Invoke(Id); -> Remove from application!
|
||||
FinalizeEvents();
|
||||
Terminated?.Invoke(Id);
|
||||
}
|
||||
|
||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var changed = false;
|
||||
var openWindows = nativeMethods.GetOpenWindows();
|
||||
|
||||
lock (@lock)
|
||||
{
|
||||
var closedWindows = windows.Where(w => openWindows.All(ow => ow != w.Handle)).ToList();
|
||||
var openedWindows = openWindows.Where(ow => windows.All(w => w.Handle != ow) && BelongsToInstance(ow));
|
||||
|
||||
foreach (var window in closedWindows)
|
||||
{
|
||||
changed = true;
|
||||
windows.Remove(window);
|
||||
}
|
||||
|
||||
foreach (var window in openedWindows)
|
||||
{
|
||||
changed = true;
|
||||
windows.Add(new ExternalApplicationWindow(icon, nativeMethods, window));
|
||||
}
|
||||
|
||||
foreach (var window in windows)
|
||||
{
|
||||
window.Update();
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
logger.Error("WINDOWS CHANGED!");
|
||||
WindowsChanged?.Invoke();
|
||||
}
|
||||
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private bool BelongsToInstance(IntPtr window)
|
||||
{
|
||||
return nativeMethods.GetProcessIdFor(window) == process.Id;
|
||||
}
|
||||
|
||||
private void InitializeEvents()
|
||||
{
|
||||
const int ONE_SECOND = 1000;
|
||||
|
||||
process.Terminated += Process_Terminated;
|
||||
|
||||
timer = new Timer(ONE_SECOND);
|
||||
timer.Elapsed += Timer_Elapsed;
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void FinalizeEvents()
|
||||
{
|
||||
timer.Elapsed -= Timer_Elapsed;
|
||||
timer.Stop();
|
||||
|
||||
process.Terminated -= Process_Terminated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
52
SafeExamBrowser.Applications/ExternalApplicationWindow.cs
Normal file
52
SafeExamBrowser.Applications/ExternalApplicationWindow.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Applications.Contracts.Events;
|
||||
using SafeExamBrowser.Core.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Applications
|
||||
{
|
||||
internal class ExternalApplicationWindow : IApplicationWindow
|
||||
{
|
||||
private INativeMethods nativeMethods;
|
||||
|
||||
internal IntPtr Handle { get; }
|
||||
public IconResource Icon { get; }
|
||||
public string Title { get; private set; }
|
||||
|
||||
public event IconChangedEventHandler IconChanged { add { } remove { } }
|
||||
public event TitleChangedEventHandler TitleChanged;
|
||||
|
||||
internal ExternalApplicationWindow(IconResource icon, INativeMethods nativeMethods, IntPtr handle)
|
||||
{
|
||||
this.Handle = handle;
|
||||
this.Icon = icon;
|
||||
this.nativeMethods = nativeMethods;
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
nativeMethods.ActivateWindow(Handle);
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
var title = nativeMethods.GetWindowTitle(Handle);
|
||||
var hasChanged = Title?.Equals(title, StringComparison.Ordinal) != true;
|
||||
|
||||
if (hasChanged)
|
||||
{
|
||||
Title = title;
|
||||
TitleChanged?.Invoke(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,9 +55,10 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ApplicationFactory.cs" />
|
||||
<Compile Include="ApplicationInstanceIdentifier.cs" />
|
||||
<Compile Include="Events\InstanceTerminatedEventHandler.cs" />
|
||||
<Compile Include="ExternalApplication.cs" />
|
||||
<Compile Include="ExternalApplicationInstance.cs" />
|
||||
<Compile Include="ExternalApplicationWindow.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -112,9 +112,9 @@ namespace SafeExamBrowser.Browser
|
|||
|
||||
private void CreateNewInstance(string url = null)
|
||||
{
|
||||
var id = new BrowserInstanceIdentifier(++instanceIdCounter);
|
||||
var id = ++instanceIdCounter;
|
||||
var isMainInstance = instances.Count == 0;
|
||||
var instanceLogger = logger.CloneFor($"BrowserInstance {id}");
|
||||
var instanceLogger = logger.CloneFor($"Browser Instance #{id}");
|
||||
var startUrl = url ?? settings.StartUrl;
|
||||
var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, messageBox, instanceLogger, text, uiFactory, startUrl);
|
||||
|
||||
|
@ -157,10 +157,9 @@ namespace SafeExamBrowser.Browser
|
|||
CreateNewInstance(args.Url);
|
||||
}
|
||||
|
||||
private void Instance_Terminated(InstanceIdentifier id)
|
||||
private void Instance_Terminated(int id)
|
||||
{
|
||||
instances.Remove(instances.FirstOrDefault(i => i.Id == id));
|
||||
logger.Info($"Browser instance {id} was terminated.");
|
||||
instances.Remove(instances.First(i => i.Id == id));
|
||||
WindowsChanged?.Invoke();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace SafeExamBrowser.Browser
|
|||
get { return isMainInstance ? settings.MainWindow : settings.AdditionalWindow; }
|
||||
}
|
||||
|
||||
internal BrowserInstanceIdentifier Id { get; private set; }
|
||||
internal int Id { get; }
|
||||
|
||||
public IconResource Icon { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
|
@ -64,7 +64,7 @@ namespace SafeExamBrowser.Browser
|
|||
public BrowserApplicationInstance(
|
||||
AppConfig appConfig,
|
||||
BrowserSettings settings,
|
||||
BrowserInstanceIdentifier id,
|
||||
int id,
|
||||
bool isMainInstance,
|
||||
IMessageBox messageBox,
|
||||
IModuleLogger logger,
|
||||
|
@ -166,7 +166,7 @@ namespace SafeExamBrowser.Browser
|
|||
private void InitializeWindow()
|
||||
{
|
||||
window = uiFactory.CreateBrowserWindow(control, settings, isMainInstance);
|
||||
window.Closing += () => Terminated?.Invoke(Id);
|
||||
window.Closing += Window_Closing;
|
||||
window.AddressChanged += Window_AddressChanged;
|
||||
window.BackwardNavigationRequested += Window_BackwardNavigationRequested;
|
||||
window.DeveloperConsoleRequested += Window_DeveloperConsoleRequested;
|
||||
|
@ -326,6 +326,12 @@ namespace SafeExamBrowser.Browser
|
|||
control.NavigateBackwards();
|
||||
}
|
||||
|
||||
private void Window_Closing()
|
||||
{
|
||||
logger.Info($"Instance has terminated.");
|
||||
Terminated?.Invoke(Id);
|
||||
}
|
||||
|
||||
private void Window_DeveloperConsoleRequested()
|
||||
{
|
||||
logger.Debug("Showing developer console...");
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* 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.Browser
|
||||
{
|
||||
internal class BrowserInstanceIdentifier : InstanceIdentifier
|
||||
{
|
||||
internal int Value { get; private set; }
|
||||
|
||||
public BrowserInstanceIdentifier(int id)
|
||||
{
|
||||
Value = id;
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (other is BrowserInstanceIdentifier id)
|
||||
{
|
||||
return Value == id.Value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"#{Value}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,5 +8,5 @@
|
|||
|
||||
namespace SafeExamBrowser.Browser.Events
|
||||
{
|
||||
internal delegate void InstanceTerminatedEventHandler(BrowserInstanceIdentifier id);
|
||||
internal delegate void InstanceTerminatedEventHandler(int id);
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="BrowserApplication.cs" />
|
||||
<Compile Include="BrowserApplicationInstance.cs" />
|
||||
<Compile Include="BrowserInstanceIdentifier.cs" />
|
||||
<Compile Include="Events\FaviconChangedEventHandler.cs" />
|
||||
<Compile Include="Events\InstanceTerminatedEventHandler.cs" />
|
||||
<Compile Include="Events\PopupRequestedEventArgs.cs" />
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace SafeExamBrowser.Client
|
|||
taskView = BuildTaskView();
|
||||
|
||||
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
|
||||
var applicationFactory = new ApplicationFactory(ModuleLogger(nameof(ApplicationFactory)), processFactory);
|
||||
var applicationFactory = new ApplicationFactory(ModuleLogger(nameof(ApplicationFactory)), nativeMethods, 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 explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
||||
|
|
|
@ -145,7 +145,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
|||
Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
|
||||
Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top;
|
||||
|
||||
if (!windows.Any())
|
||||
if (!controls.Any())
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
|||
Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
|
||||
Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top;
|
||||
|
||||
if (!windows.Any())
|
||||
if (!controls.Any())
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,11 @@ namespace SafeExamBrowser.WindowsApi.Contracts
|
|||
/// </summary>
|
||||
public interface INativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// Brings the window with the given handle to the foreground and activates it.
|
||||
/// </summary>
|
||||
void ActivateWindow(IntPtr handle);
|
||||
|
||||
/// <summary>
|
||||
/// Deregisters a previously registered keyboard hook.
|
||||
/// </summary>
|
||||
|
@ -86,7 +91,7 @@ namespace SafeExamBrowser.WindowsApi.Contracts
|
|||
string GetWallpaperPath();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the title of the specified window, or an empty string, if the given window does not have a title.
|
||||
/// Retrieves the title of the window with the given handle, or an empty string if the given window does not have a title.
|
||||
/// </summary>
|
||||
string GetWindowTitle(IntPtr window);
|
||||
|
||||
|
|
|
@ -24,34 +24,50 @@ namespace SafeExamBrowser.WindowsApi
|
|||
{
|
||||
public class NativeMethods : INativeMethods
|
||||
{
|
||||
private ConcurrentBag<KeyboardHook> KeyboardHooks = new ConcurrentBag<KeyboardHook>();
|
||||
private ConcurrentBag<MouseHook> MouseHooks = new ConcurrentBag<MouseHook>();
|
||||
private ConcurrentBag<SystemHook> SystemHooks = new ConcurrentBag<SystemHook>();
|
||||
private ConcurrentDictionary<Guid, KeyboardHook> KeyboardHooks = new ConcurrentDictionary<Guid, KeyboardHook>();
|
||||
private ConcurrentDictionary<Guid, MouseHook> MouseHooks = new ConcurrentDictionary<Guid, MouseHook>();
|
||||
private ConcurrentDictionary<Guid, SystemHook> SystemHooks = new ConcurrentDictionary<Guid, SystemHook>();
|
||||
|
||||
/// <summary>
|
||||
/// Upon finalization, unregister all active system events and hooks...
|
||||
/// </summary>
|
||||
~NativeMethods()
|
||||
{
|
||||
foreach (var hook in SystemHooks)
|
||||
foreach (var hook in SystemHooks.Values)
|
||||
{
|
||||
hook.Detach();
|
||||
}
|
||||
|
||||
foreach (var hook in KeyboardHooks)
|
||||
foreach (var hook in KeyboardHooks.Values)
|
||||
{
|
||||
hook.Detach();
|
||||
}
|
||||
|
||||
foreach (var hook in MouseHooks)
|
||||
foreach (var hook in MouseHooks.Values)
|
||||
{
|
||||
hook.Detach();
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivateWindow(IntPtr handle)
|
||||
{
|
||||
var placement = new WINDOWPLACEMENT();
|
||||
|
||||
User32.BringWindowToTop(handle);
|
||||
User32.SetForegroundWindow(handle);
|
||||
|
||||
placement.length = Marshal.SizeOf(placement);
|
||||
User32.GetWindowPlacement(handle, ref placement);
|
||||
|
||||
if (placement.showCmd == (int) ShowWindowCommand.ShowMinimized)
|
||||
{
|
||||
User32.ShowWindowAsync(handle, (int) ShowWindowCommand.Restore);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeregisterKeyboardHook(Guid hookId)
|
||||
{
|
||||
var hook = KeyboardHooks.FirstOrDefault(h => h.Id == hookId);
|
||||
var hook = KeyboardHooks.Values.FirstOrDefault(h => h.Id == hookId);
|
||||
|
||||
if (hook != null)
|
||||
{
|
||||
|
@ -62,13 +78,13 @@ namespace SafeExamBrowser.WindowsApi
|
|||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
KeyboardHooks.TryTake(out _);
|
||||
KeyboardHooks.TryRemove(hookId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeregisterMouseHook(Guid hookId)
|
||||
{
|
||||
var hook = MouseHooks.FirstOrDefault(h => h.Id == hookId);
|
||||
var hook = MouseHooks.Values.FirstOrDefault(h => h.Id == hookId);
|
||||
|
||||
if (hook != null)
|
||||
{
|
||||
|
@ -79,13 +95,13 @@ namespace SafeExamBrowser.WindowsApi
|
|||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
MouseHooks.TryTake(out _);
|
||||
MouseHooks.TryRemove(hookId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeregisterSystemEventHook(Guid hookId)
|
||||
{
|
||||
var hook = SystemHooks.FirstOrDefault(h => h.Id == hookId);
|
||||
var hook = SystemHooks.Values.FirstOrDefault(h => h.Id == hookId);
|
||||
|
||||
if (hook != null)
|
||||
{
|
||||
|
@ -96,7 +112,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
SystemHooks.TryTake(out _);
|
||||
SystemHooks.TryRemove(hookId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +270,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
|
||||
hook.Attach();
|
||||
hookId = hook.Id;
|
||||
KeyboardHooks.Add(hook);
|
||||
KeyboardHooks[hookId] = hook;
|
||||
hookReadyEvent.Set();
|
||||
|
||||
while (true)
|
||||
|
@ -283,7 +299,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
|
||||
hook.Attach();
|
||||
hookId = hook.Id;
|
||||
MouseHooks.Add(hook);
|
||||
MouseHooks[hookId] = hook;
|
||||
hookReadyEvent.Set();
|
||||
|
||||
while (true)
|
||||
|
@ -321,7 +337,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
|
||||
hook.Attach();
|
||||
hookId = hook.Id;
|
||||
SystemHooks.Add(hook);
|
||||
SystemHooks[hookId] = hook;
|
||||
hookReadyEvent.Set();
|
||||
hook.AwaitDetach();
|
||||
});
|
||||
|
|
|
@ -11,12 +11,9 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
||||
using SafeExamBrowser.WindowsApi.Types;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
|
@ -68,34 +65,6 @@ namespace SafeExamBrowser.WindowsApi
|
|||
this.namesInitialized = true;
|
||||
}
|
||||
|
||||
public bool TryActivate()
|
||||
{
|
||||
try
|
||||
{
|
||||
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.ShowWindowAsync(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)
|
||||
{
|
||||
try
|
||||
|
@ -124,25 +93,6 @@ namespace SafeExamBrowser.WindowsApi
|
|||
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)
|
||||
{
|
||||
try
|
||||
|
|
Loading…
Reference in a new issue