SEBWIN-220: Re-integrated kiosk mode "Disable Explorer Shell".
This commit is contained in:
parent
729133ac78
commit
d521e2d3c0
16 changed files with 220 additions and 133 deletions
|
@ -9,6 +9,7 @@
|
|||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Behaviour.Operations;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
|
||||
|
@ -17,46 +18,57 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
|
|||
[TestClass]
|
||||
public class ProcessMonitorOperationTests
|
||||
{
|
||||
private Mock<ILogger> loggerMock;
|
||||
private Mock<IProcessMonitor> processMonitorMock;
|
||||
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IProcessMonitor> processMonitor;
|
||||
private Settings settings;
|
||||
private ProcessMonitorOperation sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
loggerMock = new Mock<ILogger>();
|
||||
processMonitorMock = new Mock<IProcessMonitor>();
|
||||
logger = new Mock<ILogger>();
|
||||
processMonitor = new Mock<IProcessMonitor>();
|
||||
settings = new Settings();
|
||||
|
||||
sut = new ProcessMonitorOperation(loggerMock.Object, processMonitorMock.Object);
|
||||
sut = new ProcessMonitorOperation(logger.Object, processMonitor.Object,settings);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustPerformCorrectly()
|
||||
public void MustObserveExplorerWithDisableExplorerShell()
|
||||
{
|
||||
var order = 0;
|
||||
var counter = 0;
|
||||
var start = 0;
|
||||
var stop = 0;
|
||||
|
||||
processMonitorMock.Setup(p => p.CloseExplorerShell()).Callback(() => Assert.AreEqual(++order, 1));
|
||||
processMonitorMock.Setup(p => p.StartMonitoringExplorer()).Callback(() => Assert.AreEqual(++order, 2));
|
||||
settings.KioskMode = KioskMode.DisableExplorerShell;
|
||||
processMonitor.Setup(p => p.StartMonitoringExplorer()).Callback(() => start = ++counter);
|
||||
processMonitor.Setup(p => p.StopMonitoringExplorer()).Callback(() => stop = ++counter);
|
||||
|
||||
sut.Perform();
|
||||
sut.Revert();
|
||||
|
||||
processMonitorMock.Verify(p => p.CloseExplorerShell(), Times.Once);
|
||||
processMonitorMock.Verify(p => p.StartMonitoringExplorer(), Times.Once);
|
||||
processMonitor.Verify(p => p.StartMonitoringExplorer(), Times.Once);
|
||||
processMonitor.Verify(p => p.StopMonitoringExplorer(), Times.Once);
|
||||
|
||||
Assert.AreEqual(1, start);
|
||||
Assert.AreEqual(2, stop);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustRevertCorrectly()
|
||||
public void MustNotObserveExplorerWithOtherKioskModes()
|
||||
{
|
||||
var order = 0;
|
||||
|
||||
processMonitorMock.Setup(p => p.StopMonitoringExplorer()).Callback(() => Assert.AreEqual(++order, 1));
|
||||
processMonitorMock.Setup(p => p.StartExplorerShell()).Callback(() => Assert.AreEqual(++order, 2));
|
||||
settings.KioskMode = KioskMode.CreateNewDesktop;
|
||||
|
||||
sut.Perform();
|
||||
sut.Revert();
|
||||
|
||||
processMonitorMock.Verify(p => p.StopMonitoringExplorer(), Times.Once);
|
||||
processMonitorMock.Verify(p => p.StartExplorerShell(), Times.Once);
|
||||
settings.KioskMode = KioskMode.None;
|
||||
|
||||
sut.Perform();
|
||||
sut.Revert();
|
||||
|
||||
processMonitor.Verify(p => p.StartMonitoringExplorer(), Times.Never);
|
||||
processMonitor.Verify(p => p.StopMonitoringExplorer(), Times.Never);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,14 @@ using SafeExamBrowser.Contracts.UserInterface;
|
|||
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Windows;
|
||||
using SafeExamBrowser.Contracts.WindowsApi;
|
||||
|
||||
namespace SafeExamBrowser.Client.Behaviour
|
||||
{
|
||||
internal class ClientController : IClientController
|
||||
{
|
||||
private IDisplayMonitor displayMonitor;
|
||||
private IExplorerShell explorerShell;
|
||||
private ILogger logger;
|
||||
private IMessageBox messageBox;
|
||||
private IOperationSequence operations;
|
||||
|
@ -64,6 +66,7 @@ namespace SafeExamBrowser.Client.Behaviour
|
|||
|
||||
public ClientController(
|
||||
IDisplayMonitor displayMonitor,
|
||||
IExplorerShell explorerShell,
|
||||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
IOperationSequence operations,
|
||||
|
@ -76,6 +79,7 @@ namespace SafeExamBrowser.Client.Behaviour
|
|||
IWindowMonitor windowMonitor)
|
||||
{
|
||||
this.displayMonitor = displayMonitor;
|
||||
this.explorerShell = explorerShell;
|
||||
this.logger = logger;
|
||||
this.messageBox = messageBox;
|
||||
this.operations = operations;
|
||||
|
@ -188,8 +192,8 @@ namespace SafeExamBrowser.Client.Behaviour
|
|||
|
||||
private void ProcessMonitor_ExplorerStarted()
|
||||
{
|
||||
logger.Info("Trying to shut down explorer...");
|
||||
processMonitor.CloseExplorerShell();
|
||||
logger.Info("Trying to terminate Windows explorer...");
|
||||
explorerShell.Terminate();
|
||||
logger.Info("Reinitializing working area...");
|
||||
displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight());
|
||||
logger.Info("Reinitializing taskbar bounds...");
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
using SafeExamBrowser.Contracts.Behaviour.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
|
@ -18,26 +19,26 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
|
|||
{
|
||||
private ILogger logger;
|
||||
private IProcessMonitor processMonitor;
|
||||
private Settings settings;
|
||||
|
||||
public IProgressIndicator ProgressIndicator { private get; set; }
|
||||
|
||||
public ProcessMonitorOperation(ILogger logger, IProcessMonitor processMonitor)
|
||||
public ProcessMonitorOperation(ILogger logger, IProcessMonitor processMonitor, Settings settings)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.processMonitor = processMonitor;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing process monitoring...");
|
||||
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_WaitExplorerTermination, true);
|
||||
|
||||
processMonitor.CloseExplorerShell();
|
||||
processMonitor.StartMonitoringExplorer();
|
||||
|
||||
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeProcessMonitoring);
|
||||
|
||||
// TODO: Implement process monitoring...
|
||||
if (settings.KioskMode == KioskMode.DisableExplorerShell)
|
||||
{
|
||||
processMonitor.StartMonitoringExplorer();
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
@ -52,12 +53,10 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
|
|||
logger.Info("Stopping process monitoring...");
|
||||
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StopProcessMonitoring);
|
||||
|
||||
// TODO: Implement process monitoring...
|
||||
|
||||
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_WaitExplorerStartup, true);
|
||||
|
||||
processMonitor.StopMonitoringExplorer();
|
||||
processMonitor.StartExplorerShell();
|
||||
if (settings.KioskMode == KioskMode.DisableExplorerShell)
|
||||
{
|
||||
processMonitor.StopMonitoringExplorer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ using SafeExamBrowser.Contracts.Communication.Proxies;
|
|||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||
using SafeExamBrowser.Contracts.WindowsApi;
|
||||
|
@ -53,6 +54,7 @@ namespace SafeExamBrowser.Client
|
|||
private IClientHost clientHost;
|
||||
private ILogger logger;
|
||||
private IMessageBox messageBox;
|
||||
private IProcessMonitor processMonitor;
|
||||
private INativeMethods nativeMethods;
|
||||
private IRuntimeProxy runtimeProxy;
|
||||
private ISystemInfo systemInfo;
|
||||
|
@ -75,11 +77,12 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
text = new Text(logger);
|
||||
messageBox = new MessageBox(text);
|
||||
processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
|
||||
uiFactory = new UserInterfaceFactory(text);
|
||||
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, typeof(RuntimeProxy)));
|
||||
|
||||
var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
|
||||
var processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
|
||||
var explorerShell = new ExplorerShell(new ModuleLogger(logger, typeof(ExplorerShell)), nativeMethods);
|
||||
var windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods);
|
||||
|
||||
Taskbar = new Taskbar(new ModuleLogger(logger, typeof(Taskbar)));
|
||||
|
@ -94,7 +97,7 @@ namespace SafeExamBrowser.Client
|
|||
// TODO
|
||||
//operations.Enqueue(new DelayedInitializationOperation(BuildKeyboardInterceptorOperation));
|
||||
//operations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
|
||||
//operations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
|
||||
operations.Enqueue(new DelayedInitializationOperation(BuildProcessMonitorOperation));
|
||||
operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar));
|
||||
operations.Enqueue(new DelayedInitializationOperation(BuildTaskbarOperation));
|
||||
operations.Enqueue(new DelayedInitializationOperation(BuildBrowserOperation));
|
||||
|
@ -104,7 +107,7 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
var sequence = new OperationSequence(logger, operations);
|
||||
|
||||
ClientController = new ClientController(displayMonitor, logger, messageBox, sequence, processMonitor, runtimeProxy, shutdown, Taskbar, text, uiFactory, windowMonitor);
|
||||
ClientController = new ClientController(displayMonitor, explorerShell, logger, messageBox, sequence, processMonitor, runtimeProxy, shutdown, Taskbar, text, uiFactory, windowMonitor);
|
||||
}
|
||||
|
||||
internal void LogStartupInformation()
|
||||
|
@ -191,6 +194,11 @@ namespace SafeExamBrowser.Client
|
|||
return operation;
|
||||
}
|
||||
|
||||
private IOperation BuildProcessMonitorOperation()
|
||||
{
|
||||
return new ProcessMonitorOperation(logger, processMonitor, configuration.Settings);
|
||||
}
|
||||
|
||||
private IOperation BuildTaskbarOperation()
|
||||
{
|
||||
var keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, typeof(KeyboardLayout)), text);
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace SafeExamBrowser.Configuration
|
|||
|
||||
CurrentSettings = new Settings();
|
||||
|
||||
CurrentSettings.KioskMode = KioskMode.CreateNewDesktop;
|
||||
CurrentSettings.KioskMode = KioskMode.DisableExplorerShell;
|
||||
CurrentSettings.ServicePolicy = ServicePolicy.Optional;
|
||||
|
||||
CurrentSettings.Browser.StartUrl = "https://www.safeexambrowser.org/testing";
|
||||
|
|
|
@ -26,16 +26,6 @@ namespace SafeExamBrowser.Contracts.Monitoring
|
|||
/// </summary>
|
||||
bool BelongsToAllowedProcess(IntPtr window);
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the Windows explorer shell, i.e. the taskbar.
|
||||
/// </summary>
|
||||
void CloseExplorerShell();
|
||||
|
||||
/// <summary>
|
||||
/// Starts a new instance of the Windows explorer shell.
|
||||
/// </summary>
|
||||
void StartExplorerShell();
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring the Windows explorer, i.e. any newly created instances of <c>explorer.exe</c> will trigger the
|
||||
/// <see cref="ExplorerStarted"/> event.
|
||||
|
|
|
@ -182,6 +182,7 @@
|
|||
<Compile Include="WindowsApi\IBounds.cs" />
|
||||
<Compile Include="WindowsApi\IDesktop.cs" />
|
||||
<Compile Include="WindowsApi\IDesktopFactory.cs" />
|
||||
<Compile Include="WindowsApi\IExplorerShell.cs" />
|
||||
<Compile Include="WindowsApi\INativeMethods.cs" />
|
||||
<Compile Include="WindowsApi\IProcess.cs" />
|
||||
<Compile Include="WindowsApi\IProcessFactory.cs" />
|
||||
|
|
23
SafeExamBrowser.Contracts/WindowsApi/IExplorerShell.cs
Normal file
23
SafeExamBrowser.Contracts/WindowsApi/IExplorerShell.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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.Contracts.WindowsApi
|
||||
{
|
||||
public interface IExplorerShell
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts the Windows explorer shell, if it isn't already running.
|
||||
/// </summary>
|
||||
void Start();
|
||||
|
||||
/// <summary>
|
||||
/// Gracefully terminates the Windows explorer shell, if it is running.
|
||||
/// </summary>
|
||||
void Terminate();
|
||||
}
|
||||
}
|
|
@ -8,10 +8,7 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.Monitoring;
|
||||
using SafeExamBrowser.Contracts.Monitoring.Events;
|
||||
|
@ -53,53 +50,6 @@ namespace SafeExamBrowser.Monitoring.Processes
|
|||
return true;
|
||||
}
|
||||
|
||||
public void CloseExplorerShell()
|
||||
{
|
||||
var processId = nativeMethods.GetShellProcessId();
|
||||
var explorerProcesses = Process.GetProcessesByName("explorer");
|
||||
var shellProcess = explorerProcesses.FirstOrDefault(p => p.Id == processId);
|
||||
|
||||
if (shellProcess != null)
|
||||
{
|
||||
logger.Info($"Found explorer shell processes with PID = {processId}. Sending close message...");
|
||||
|
||||
nativeMethods.PostCloseMessageToShell();
|
||||
|
||||
while (!shellProcess.HasExited)
|
||||
{
|
||||
shellProcess.Refresh();
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
logger.Info($"Successfully terminated explorer shell process with PID = {processId}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("The explorer shell seems to already be terminated. Skipping this step...");
|
||||
}
|
||||
}
|
||||
|
||||
public void StartExplorerShell()
|
||||
{
|
||||
var process = new Process();
|
||||
var explorerPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe");
|
||||
|
||||
logger.Info("Restarting explorer shell...");
|
||||
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.StartInfo.FileName = explorerPath;
|
||||
process.Start();
|
||||
|
||||
while (nativeMethods.GetShellWindowHandle() == IntPtr.Zero)
|
||||
{
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
process.Refresh();
|
||||
logger.Info($"Explorer shell successfully started with PID = {process.Id}.");
|
||||
process.Close();
|
||||
}
|
||||
|
||||
public void StartMonitoringExplorer()
|
||||
{
|
||||
explorerWatcher = new ManagementEventWatcher(@"\\.\root\CIMV2", GetQueryFor("explorer.exe"));
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
{
|
||||
private IConfigurationRepository configuration;
|
||||
private IDesktopFactory desktopFactory;
|
||||
private IExplorerShell explorerShell;
|
||||
private KioskMode kioskMode;
|
||||
private ILogger logger;
|
||||
private IProcessFactory processFactory;
|
||||
|
@ -31,11 +32,13 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
public KioskModeOperation(
|
||||
IConfigurationRepository configuration,
|
||||
IDesktopFactory desktopFactory,
|
||||
IExplorerShell explorerShell,
|
||||
ILogger logger,
|
||||
IProcessFactory processFactory)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
this.desktopFactory = desktopFactory;
|
||||
this.explorerShell = explorerShell;
|
||||
this.logger = logger;
|
||||
this.processFactory = processFactory;
|
||||
}
|
||||
|
@ -53,7 +56,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
CreateNewDesktop();
|
||||
break;
|
||||
case KioskMode.DisableExplorerShell:
|
||||
DisableExplorerShell();
|
||||
TerminateExplorerShell();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -62,7 +65,20 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
|
||||
public OperationResult Repeat()
|
||||
{
|
||||
// TODO: Depends on new kiosk mode!
|
||||
var oldMode = kioskMode;
|
||||
var newMode = configuration.CurrentSettings.KioskMode;
|
||||
|
||||
if (newMode == oldMode)
|
||||
{
|
||||
logger.Info($"New kiosk mode '{newMode}' is equal to the currently active '{oldMode}', skipping re-initialization...");
|
||||
}
|
||||
else
|
||||
{
|
||||
Revert();
|
||||
Perform();
|
||||
}
|
||||
|
||||
kioskMode = newMode;
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
@ -86,12 +102,14 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
private void CreateNewDesktop()
|
||||
{
|
||||
originalDesktop = desktopFactory.GetCurrent();
|
||||
logger.Info($"Current desktop is {ToString(originalDesktop)}.");
|
||||
logger.Info($"Current desktop is {originalDesktop}.");
|
||||
|
||||
newDesktop = desktopFactory.CreateNew(nameof(SafeExamBrowser));
|
||||
logger.Info($"Created new desktop {ToString(newDesktop)}.");
|
||||
logger.Info($"Created new desktop {newDesktop}.");
|
||||
|
||||
newDesktop.Activate();
|
||||
logger.Info("Successfully activated new desktop.");
|
||||
processFactory.StartupDesktop = newDesktop;
|
||||
logger.Info("Successfully activated new desktop.");
|
||||
}
|
||||
|
||||
private void CloseNewDesktop()
|
||||
|
@ -100,37 +118,34 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
{
|
||||
originalDesktop.Activate();
|
||||
processFactory.StartupDesktop = originalDesktop;
|
||||
logger.Info($"Switched back to original desktop {ToString(originalDesktop)}.");
|
||||
logger.Info($"Switched back to original desktop {originalDesktop}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"No original desktop found when attempting to revert kiosk mode '{kioskMode}'!");
|
||||
logger.Warn($"No original desktop found when attempting to close new desktop!");
|
||||
}
|
||||
|
||||
if (newDesktop != null)
|
||||
{
|
||||
newDesktop.Close();
|
||||
logger.Info($"Closed new desktop {ToString(newDesktop)}.");
|
||||
logger.Info($"Closed new desktop {newDesktop}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"No new desktop found when attempting to revert kiosk mode '{kioskMode}'!");
|
||||
logger.Warn($"No new desktop found when attempting to close new desktop!");
|
||||
}
|
||||
}
|
||||
|
||||
private void DisableExplorerShell()
|
||||
private void TerminateExplorerShell()
|
||||
{
|
||||
// TODO
|
||||
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_WaitExplorerTermination, true);
|
||||
explorerShell.Terminate();
|
||||
}
|
||||
|
||||
private void RestartExplorerShell()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
private string ToString(IDesktop desktop)
|
||||
{
|
||||
return $"'{desktop.Name}' [{desktop.Handle}]";
|
||||
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_WaitExplorerStartup, true);
|
||||
explorerShell.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ namespace SafeExamBrowser.Runtime
|
|||
var text = new Text(logger);
|
||||
var messageBox = new MessageBox(text);
|
||||
var desktopFactory = new DesktopFactory(new ModuleLogger(logger, typeof(DesktopFactory)));
|
||||
var explorerShell = new ExplorerShell(new ModuleLogger(logger, typeof(ExplorerShell)), nativeMethods);
|
||||
var processFactory = new ProcessFactory(new ModuleLogger(logger, typeof(ProcessFactory)));
|
||||
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger);
|
||||
var resourceLoader = new ResourceLoader();
|
||||
|
@ -68,7 +69,7 @@ namespace SafeExamBrowser.Runtime
|
|||
sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost));
|
||||
sessionOperations.Enqueue(new ServiceOperation(configuration, logger, serviceProxy, text));
|
||||
sessionOperations.Enqueue(new ClientTerminationOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, TEN_SECONDS));
|
||||
sessionOperations.Enqueue(new KioskModeOperation(configuration, desktopFactory, logger, processFactory));
|
||||
sessionOperations.Enqueue(new KioskModeOperation(configuration, desktopFactory, explorerShell, logger, processFactory));
|
||||
sessionOperations.Enqueue(new ClientOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, TEN_SECONDS));
|
||||
|
||||
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
|
||||
|
|
|
@ -43,5 +43,10 @@ namespace SafeExamBrowser.WindowsApi
|
|||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"'{Name}' [{Handle}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,11 @@ namespace SafeExamBrowser.WindowsApi
|
|||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
logger.Debug($"Successfully created desktop '{name}' [{handle}].");
|
||||
var desktop = new Desktop(handle, name);
|
||||
|
||||
return new Desktop(handle, name);
|
||||
logger.Debug($"Successfully created desktop {desktop}.");
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
public IDesktop GetCurrent()
|
||||
|
@ -49,17 +51,15 @@ namespace SafeExamBrowser.WindowsApi
|
|||
var name = String.Empty;
|
||||
var nameLength = 0;
|
||||
|
||||
if (handle != IntPtr.Zero)
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
logger.Debug($"Found desktop handle {handle} for thread {threadId}. Attempting to get desktop name...");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to get desktop handle for thread {threadId}!");
|
||||
logger.Error($"Failed to get desktop handle for thread with ID = {threadId}!");
|
||||
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
logger.Debug($"Found desktop handle for thread with ID = {threadId}. Attempting to get desktop name...");
|
||||
|
||||
User32.GetUserObjectInformation(handle, Constant.UOI_NAME, IntPtr.Zero, 0, ref nameLength);
|
||||
|
||||
var namePointer = Marshal.AllocHGlobal(nameLength);
|
||||
|
@ -67,7 +67,7 @@ namespace SafeExamBrowser.WindowsApi
|
|||
|
||||
if (!success)
|
||||
{
|
||||
logger.Error($"Failed to retrieve name for desktop with handle {handle}!");
|
||||
logger.Error($"Failed to retrieve name for desktop with handle = {handle}!");
|
||||
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
@ -75,9 +75,11 @@ namespace SafeExamBrowser.WindowsApi
|
|||
name = Marshal.PtrToStringAnsi(namePointer);
|
||||
Marshal.FreeHGlobal(namePointer);
|
||||
|
||||
logger.Debug($"Successfully determined current desktop as '{name}' [{handle}].");
|
||||
var desktop = new Desktop(handle, name);
|
||||
|
||||
return new Desktop(handle, name);
|
||||
logger.Debug($"Successfully determined current desktop {desktop}.");
|
||||
|
||||
return desktop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
76
SafeExamBrowser.WindowsApi/ExplorerShell.cs
Normal file
76
SafeExamBrowser.WindowsApi/ExplorerShell.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.WindowsApi;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
public class ExplorerShell : IExplorerShell
|
||||
{
|
||||
private ILogger logger;
|
||||
private INativeMethods nativeMethods;
|
||||
|
||||
public ExplorerShell(ILogger logger, INativeMethods nativeMethods)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.nativeMethods = nativeMethods;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
var process = new System.Diagnostics.Process();
|
||||
var explorerPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe");
|
||||
|
||||
logger.Info("Restarting explorer shell...");
|
||||
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.StartInfo.FileName = explorerPath;
|
||||
process.Start();
|
||||
|
||||
while (nativeMethods.GetShellWindowHandle() == IntPtr.Zero)
|
||||
{
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
process.Refresh();
|
||||
logger.Info($"Explorer shell successfully started with PID = {process.Id}.");
|
||||
process.Close();
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
var processId = nativeMethods.GetShellProcessId();
|
||||
var explorerProcesses = System.Diagnostics.Process.GetProcessesByName("explorer");
|
||||
var shellProcess = explorerProcesses.FirstOrDefault(p => p.Id == processId);
|
||||
|
||||
if (shellProcess != null)
|
||||
{
|
||||
logger.Debug($"Found explorer shell processes with PID = {processId}. Sending close message...");
|
||||
|
||||
nativeMethods.PostCloseMessageToShell();
|
||||
|
||||
while (!shellProcess.HasExited)
|
||||
{
|
||||
shellProcess.Refresh();
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
logger.Info($"Successfully terminated explorer shell process with PID = {processId}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("The explorer shell seems to already be terminated. Skipping this step...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,18 +41,18 @@ namespace SafeExamBrowser.WindowsApi
|
|||
|
||||
var success = Kernel32.CreateProcess(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, Constant.NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref startupInfo, ref processInfo);
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info($"Successfully started process '{Path.GetFileName(path)}' with ID {processInfo.dwProcessId}.");
|
||||
}
|
||||
else
|
||||
if (!success)
|
||||
{
|
||||
logger.Error($"Failed to start process '{Path.GetFileName(path)}'!");
|
||||
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
return new Process(processInfo.dwProcessId);
|
||||
var process = new Process(processInfo.dwProcessId);
|
||||
|
||||
logger.Info($"Successfully started process '{Path.GetFileName(path)}' with ID = {process.Id}.");
|
||||
|
||||
return process;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
<Compile Include="Delegates\HookDelegate.cs" />
|
||||
<Compile Include="Desktop.cs" />
|
||||
<Compile Include="DesktopFactory.cs" />
|
||||
<Compile Include="ExplorerShell.cs" />
|
||||
<Compile Include="Monitoring\MouseHook.cs" />
|
||||
<Compile Include="Process.cs" />
|
||||
<Compile Include="ProcessFactory.cs" />
|
||||
|
|
Loading…
Reference in a new issue