SEBWIN-219: Basic startup sequence is now working.
This commit is contained in:
parent
d935407ecb
commit
d3dea29ecd
23 changed files with 329 additions and 114 deletions
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
|
||||
|
@ -56,14 +55,16 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
ShutdownMode = ShutdownMode.OnMainWindowClose;
|
||||
|
||||
instances.BuildObjectGraph();
|
||||
instances.LogStartupInformation();
|
||||
|
||||
var success = instances.ClientController.TryStart();
|
||||
|
||||
if (success)
|
||||
{
|
||||
MainWindow = instances.Taskbar;
|
||||
MainWindow.Closing += MainWindow_Closing;
|
||||
MainWindow.Show();
|
||||
}
|
||||
else
|
||||
|
@ -72,10 +73,12 @@ namespace SafeExamBrowser.Client
|
|||
}
|
||||
}
|
||||
|
||||
private void MainWindow_Closing(object sender, CancelEventArgs e)
|
||||
protected override void OnExit(ExitEventArgs e)
|
||||
{
|
||||
MainWindow?.Hide();
|
||||
instances.ClientController.Terminate();
|
||||
instances.LogShutdownInformation();
|
||||
|
||||
base.OnExit(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,13 @@ namespace SafeExamBrowser.Client.Communication
|
|||
{
|
||||
internal class ClientHost : BaseHost, IClientHost
|
||||
{
|
||||
private int processId;
|
||||
|
||||
public Guid StartupToken { private get; set; }
|
||||
|
||||
public ClientHost(string address, ILogger logger) : base(address, logger)
|
||||
public ClientHost(string address, ILogger logger, int processId) : base(address, logger)
|
||||
{
|
||||
this.processId = processId;
|
||||
}
|
||||
|
||||
protected override bool OnConnect(Guid? token)
|
||||
|
@ -36,13 +39,19 @@ namespace SafeExamBrowser.Client.Communication
|
|||
protected override Response OnReceive(Message message)
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
}
|
||||
|
||||
protected override Response OnReceive(MessagePurport message)
|
||||
protected override Response OnReceive(SimpleMessagePurport message)
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
switch (message)
|
||||
{
|
||||
case SimpleMessagePurport.Authenticate:
|
||||
return new AuthenticationResponse { ProcessId = processId };
|
||||
}
|
||||
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using SafeExamBrowser.Browser;
|
||||
using SafeExamBrowser.Client.Behaviour;
|
||||
using SafeExamBrowser.Client.Behaviour.Operations;
|
||||
|
@ -37,6 +38,10 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
internal class CompositionRoot
|
||||
{
|
||||
private string logFilePath;
|
||||
private string runtimeHostUri;
|
||||
private Guid startupToken;
|
||||
|
||||
private ClientConfiguration configuration;
|
||||
private ILogger logger;
|
||||
private INativeMethods nativeMethods;
|
||||
|
@ -49,21 +54,19 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
internal void BuildObjectGraph()
|
||||
{
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
|
||||
Validate(args);
|
||||
ValidateCommandLineArguments();
|
||||
|
||||
configuration = new ClientConfiguration();
|
||||
logger = new Logger();
|
||||
nativeMethods = new NativeMethods();
|
||||
systemInfo = new SystemInfo();
|
||||
|
||||
InitializeLogging(args[1]);
|
||||
InitializeLogging();
|
||||
|
||||
text = new Text(logger);
|
||||
uiFactory = new UserInterfaceFactory(text);
|
||||
|
||||
var runtimeProxy = new RuntimeProxy(args[2], new ModuleLogger(logger, typeof(RuntimeProxy)));
|
||||
var runtimeProxy = new RuntimeProxy(runtimeHostUri, 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 windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods);
|
||||
|
@ -73,25 +76,38 @@ namespace SafeExamBrowser.Client
|
|||
var operations = new Queue<IOperation>();
|
||||
|
||||
operations.Enqueue(new I18nOperation(logger, text));
|
||||
operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, Guid.Parse(args[3])));
|
||||
operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, startupToken));
|
||||
operations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeProxy));
|
||||
operations.Enqueue(new DelayedInitializationOperation(BuildCommunicationHostOperation));
|
||||
operations.Enqueue(new DelayedInitializationOperation(BuildKeyboardInterceptorOperation));
|
||||
operations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
|
||||
operations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
|
||||
// TODO
|
||||
//operations.Enqueue(new DelayedInitializationOperation(BuildKeyboardInterceptorOperation));
|
||||
//operations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
|
||||
//operations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
|
||||
operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar));
|
||||
operations.Enqueue(new DelayedInitializationOperation(BuildTaskbarOperation));
|
||||
operations.Enqueue(new DelayedInitializationOperation(BuildBrowserOperation));
|
||||
operations.Enqueue(new ClipboardOperation(logger, nativeMethods));
|
||||
operations.Enqueue(new DelayedInitializationOperation(BuildMouseInterceptorOperation));
|
||||
//operations.Enqueue(new DelayedInitializationOperation(BuildMouseInterceptorOperation));
|
||||
|
||||
var sequence = new OperationSequence(logger, operations);
|
||||
|
||||
ClientController = new ClientController(displayMonitor, logger, sequence, processMonitor, runtimeProxy, Taskbar, windowMonitor);
|
||||
}
|
||||
|
||||
private void Validate(string[] args)
|
||||
internal void LogStartupInformation()
|
||||
{
|
||||
logger.Log($"# New client instance started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||
logger.Log(string.Empty);
|
||||
}
|
||||
|
||||
internal void LogShutdownInformation()
|
||||
{
|
||||
logger?.Log($"# Client instance terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||
}
|
||||
|
||||
private void ValidateCommandLineArguments()
|
||||
{
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
var hasFour = args?.Length == 4;
|
||||
|
||||
if (hasFour)
|
||||
|
@ -102,16 +118,20 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
if (hasLogfilePath && hasHostUri && hasToken)
|
||||
{
|
||||
logFilePath = args[1];
|
||||
runtimeHostUri = args[2];
|
||||
startupToken = Guid.Parse(args[3]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException("Invalid parameters! Required: SafeExamBrowser.Client.exe <logfile path> <host URI> <token>");
|
||||
throw new ArgumentException("Invalid arguments! Required: SafeExamBrowser.Client.exe <logfile path> <host URI> <token>");
|
||||
}
|
||||
|
||||
private void InitializeLogging(string filePath)
|
||||
private void InitializeLogging()
|
||||
{
|
||||
var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), filePath);
|
||||
var logFileWriter = new LogFileWriter(new DefaultLogFormatter(), logFilePath);
|
||||
|
||||
logFileWriter.Initialize();
|
||||
logger.Subscribe(logFileWriter);
|
||||
|
@ -128,9 +148,12 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
private IOperation BuildCommunicationHostOperation()
|
||||
{
|
||||
var host = new ClientHost(configuration.RuntimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientHost)));
|
||||
var processId = Process.GetCurrentProcess().Id;
|
||||
var host = new ClientHost(configuration.RuntimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientHost)), processId);
|
||||
var operation = new CommunicationOperation(host, logger);
|
||||
|
||||
host.StartupToken = startupToken;
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ namespace SafeExamBrowser.Configuration
|
|||
};
|
||||
|
||||
settings.Browser.StartUrl = "https://www.duckduckgo.com";
|
||||
settings.Taskbar.AllowApplicationLog = true;
|
||||
settings.Taskbar.AllowKeyboardLayout = true;
|
||||
settings.Taskbar.AllowWirelessNetwork = true;
|
||||
|
||||
CurrentSettings = settings;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace SafeExamBrowser.Contracts.Communication
|
|||
[ServiceKnownType(typeof(AuthenticationResponse))]
|
||||
[ServiceKnownType(typeof(ConfigurationResponse))]
|
||||
[ServiceKnownType(typeof(ClientConfiguration))]
|
||||
[ServiceKnownType(typeof(SimpleResponse))]
|
||||
public interface ICommunication
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -20,11 +20,13 @@ namespace SafeExamBrowser.Contracts.Communication
|
|||
/// <summary>
|
||||
/// Starts the host and opens it for communication.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ServiceModel.CommunicationException">If the host fails to start.</exception>
|
||||
void Start();
|
||||
|
||||
/// <summary>
|
||||
/// Closes and terminates the host.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ServiceModel.CommunicationException">If the host fails to terminate.</exception>
|
||||
void Stop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,5 +17,10 @@ namespace SafeExamBrowser.Contracts.Communication.Messages
|
|||
/// The communication token needed for authentication.
|
||||
/// </summary>
|
||||
public Guid CommunicationToken { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,16 @@ namespace SafeExamBrowser.Contracts.Communication.Messages
|
|||
/// <summary>
|
||||
/// The purport of the message.
|
||||
/// </summary>
|
||||
public MessagePurport Purport { get; set; }
|
||||
public SimpleMessagePurport Purport { get; set; }
|
||||
|
||||
public SimpleMessage(SimpleMessagePurport purport)
|
||||
{
|
||||
Purport = purport;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{base.ToString()} -> {Purport}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ using System;
|
|||
namespace SafeExamBrowser.Contracts.Communication.Messages
|
||||
{
|
||||
[Serializable]
|
||||
public enum MessagePurport
|
||||
public enum SimpleMessagePurport
|
||||
{
|
||||
/// <summary>
|
||||
/// Requests an interlocutor to submit data for authentication.
|
|
@ -16,6 +16,6 @@ namespace SafeExamBrowser.Contracts.Communication.Responses
|
|||
/// <summary>
|
||||
/// The process identifier used for authentication.
|
||||
/// </summary>
|
||||
public int ProcessId { get; }
|
||||
public int ProcessId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,5 +13,9 @@ namespace SafeExamBrowser.Contracts.Communication.Responses
|
|||
[Serializable]
|
||||
public class Response
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Communication.Responses
|
||||
{
|
||||
[Serializable]
|
||||
public class SimpleResponse : Response
|
||||
{
|
||||
/// <summary>
|
||||
/// The purport of the response.
|
||||
/// </summary>
|
||||
public SimpleResponsePurport Purport { get; set; }
|
||||
|
||||
public SimpleResponse(SimpleResponsePurport purport)
|
||||
{
|
||||
Purport = purport;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{base.ToString()} -> {Purport}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Communication.Responses
|
||||
{
|
||||
[Serializable]
|
||||
public enum SimpleResponsePurport
|
||||
{
|
||||
/// <summary>
|
||||
/// Signals an interlocutor that a message has been understood.
|
||||
/// </summary>
|
||||
Acknowledged = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Signals an interlocutor that a message has not been understood.
|
||||
/// </summary>
|
||||
UnknownMessage
|
||||
}
|
||||
}
|
|
@ -66,13 +66,15 @@
|
|||
<Compile Include="Communication\IServiceProxy.cs" />
|
||||
<Compile Include="Communication\Messages\Message.cs" />
|
||||
<Compile Include="Communication\Messages\DisconnectionMessage.cs" />
|
||||
<Compile Include="Communication\Messages\MessagePurport.cs" />
|
||||
<Compile Include="Communication\Messages\SimpleMessagePurport.cs" />
|
||||
<Compile Include="Communication\Messages\SimpleMessage.cs" />
|
||||
<Compile Include="Communication\Responses\AuthenticationResponse.cs" />
|
||||
<Compile Include="Communication\Responses\ConfigurationResponse.cs" />
|
||||
<Compile Include="Communication\Responses\ConnectionResponse.cs" />
|
||||
<Compile Include="Communication\Responses\DisconnectionResponse.cs" />
|
||||
<Compile Include="Communication\Responses\Response.cs" />
|
||||
<Compile Include="Communication\Responses\SimpleResponsePurport.cs" />
|
||||
<Compile Include="Communication\Responses\SimpleResponse.cs" />
|
||||
<Compile Include="Configuration\ClientConfiguration.cs" />
|
||||
<Compile Include="Configuration\RuntimeInfo.cs" />
|
||||
<Compile Include="Configuration\ISession.cs" />
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
using System;
|
||||
using System.ServiceModel;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Communication.Messages;
|
||||
using SafeExamBrowser.Contracts.Communication.Responses;
|
||||
|
@ -18,16 +19,26 @@ namespace SafeExamBrowser.Core.Communication
|
|||
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
|
||||
public abstract class BaseHost : ICommunication, ICommunicationHost
|
||||
{
|
||||
private const int TWO_SECONDS = 2000;
|
||||
private readonly object @lock = new object();
|
||||
|
||||
private string address;
|
||||
private ILogger logger;
|
||||
private ServiceHost host;
|
||||
private Thread hostThread;
|
||||
|
||||
protected Guid? CommunicationToken { get; private set; }
|
||||
protected ILogger Logger { get; private set; }
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get { return host?.State == CommunicationState.Opened; }
|
||||
get
|
||||
{
|
||||
lock (@lock)
|
||||
{
|
||||
return host?.State == CommunicationState.Opened;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BaseHost(string address, ILogger logger)
|
||||
|
@ -39,86 +50,111 @@ namespace SafeExamBrowser.Core.Communication
|
|||
protected abstract bool OnConnect(Guid? token);
|
||||
protected abstract void OnDisconnect();
|
||||
protected abstract Response OnReceive(Message message);
|
||||
protected abstract Response OnReceive(MessagePurport message);
|
||||
protected abstract Response OnReceive(SimpleMessagePurport message);
|
||||
|
||||
public ConnectionResponse Connect(Guid? token = null)
|
||||
{
|
||||
logger.Debug($"Received connection request with authentication token '{token}'.");
|
||||
|
||||
var response = new ConnectionResponse();
|
||||
var connected = OnConnect(token);
|
||||
|
||||
if (connected)
|
||||
lock (@lock)
|
||||
{
|
||||
response.CommunicationToken = CommunicationToken = Guid.NewGuid();
|
||||
response.ConnectionEstablished = true;
|
||||
logger.Debug($"Received connection request with authentication token '{token}'.");
|
||||
|
||||
var response = new ConnectionResponse();
|
||||
var connected = OnConnect(token);
|
||||
|
||||
if (connected)
|
||||
{
|
||||
response.CommunicationToken = CommunicationToken = Guid.NewGuid();
|
||||
response.ConnectionEstablished = true;
|
||||
}
|
||||
|
||||
logger.Debug($"{(connected ? "Accepted" : "Denied")} connection request.");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
logger.Debug($"{(connected ? "Accepted" : "Denied")} connection request.");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public DisconnectionResponse Disconnect(DisconnectionMessage message)
|
||||
{
|
||||
var response = new DisconnectionResponse();
|
||||
|
||||
// TODO: Compare with ToString in BaseProxy - needed?
|
||||
logger.Debug($"Received disconnection request with message '{message}'.");
|
||||
|
||||
if (IsAuthorized(message?.CommunicationToken))
|
||||
lock (@lock)
|
||||
{
|
||||
OnDisconnect();
|
||||
var response = new DisconnectionResponse();
|
||||
|
||||
CommunicationToken = null;
|
||||
response.ConnectionTerminated = true;
|
||||
logger.Debug($"Received disconnection request with message '{ToString(message)}'.");
|
||||
|
||||
if (IsAuthorized(message?.CommunicationToken))
|
||||
{
|
||||
OnDisconnect();
|
||||
|
||||
CommunicationToken = null;
|
||||
response.ConnectionTerminated = true;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public Response Send(Message message)
|
||||
{
|
||||
Response response = null;
|
||||
|
||||
logger.Debug($"Received message '{message}'.");
|
||||
|
||||
if (IsAuthorized(message?.CommunicationToken))
|
||||
lock (@lock)
|
||||
{
|
||||
if (message is SimpleMessage)
|
||||
Response response = null;
|
||||
|
||||
logger.Debug($"Received message '{ToString(message)}'.");
|
||||
|
||||
if (IsAuthorized(message?.CommunicationToken))
|
||||
{
|
||||
response = OnReceive((message as SimpleMessage).Purport);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = OnReceive(message);
|
||||
if (message is SimpleMessage)
|
||||
{
|
||||
response = OnReceive((message as SimpleMessage).Purport);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = OnReceive(message);
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug($"Sending response '{ToString(response)}'.");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
logger.Debug($"Sending response '{response}'.");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
host = new ServiceHost(this);
|
||||
host.AddServiceEndpoint(typeof(ICommunication), new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), address);
|
||||
host.Closed += Host_Closed;
|
||||
host.Closing += Host_Closing;
|
||||
host.Faulted += Host_Faulted;
|
||||
host.Opened += Host_Opened;
|
||||
host.Opening += Host_Opening;
|
||||
host.UnknownMessageReceived += Host_UnknownMessageReceived;
|
||||
host.Open();
|
||||
lock (@lock)
|
||||
{
|
||||
var exception = default(Exception);
|
||||
var startedEvent = new AutoResetEvent(false);
|
||||
|
||||
logger.Debug($"Successfully started communication host for endpoint '{address}'.");
|
||||
hostThread = new Thread(() => TryStartHost(startedEvent, out exception));
|
||||
hostThread.SetApartmentState(ApartmentState.STA);
|
||||
hostThread.IsBackground = true;
|
||||
hostThread.Start();
|
||||
|
||||
var success = startedEvent.WaitOne(TWO_SECONDS);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new CommunicationException($"Failed to start communication host for endpoint '{address}' within {TWO_SECONDS / 1000} seconds!", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
host?.Close();
|
||||
logger.Debug($"Terminated communication host for endpoint '{address}'.");
|
||||
lock (@lock)
|
||||
{
|
||||
var success = TryStopHost(out Exception exception);
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Debug($"Terminated communication host for endpoint '{address}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CommunicationException($"Failed to terminate communication host for endpoint '{address}'!", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAuthorized(Guid? token)
|
||||
|
@ -126,6 +162,52 @@ namespace SafeExamBrowser.Core.Communication
|
|||
return CommunicationToken == token;
|
||||
}
|
||||
|
||||
private void TryStartHost(AutoResetEvent startedEvent, out Exception exception)
|
||||
{
|
||||
exception = null;
|
||||
|
||||
try
|
||||
{
|
||||
host = new ServiceHost(this);
|
||||
host.AddServiceEndpoint(typeof(ICommunication), new NetNamedPipeBinding(NetNamedPipeSecurityMode.Transport), address);
|
||||
host.Closed += Host_Closed;
|
||||
host.Closing += Host_Closing;
|
||||
host.Faulted += Host_Faulted;
|
||||
host.Opened += Host_Opened;
|
||||
host.Opening += Host_Opening;
|
||||
host.UnknownMessageReceived += Host_UnknownMessageReceived;
|
||||
host.Open();
|
||||
|
||||
logger.Debug($"Successfully started communication host for endpoint '{address}'.");
|
||||
|
||||
startedEvent.Set();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryStopHost(out Exception exception)
|
||||
{
|
||||
var success = false;
|
||||
|
||||
exception = null;
|
||||
|
||||
try
|
||||
{
|
||||
host?.Close();
|
||||
success = hostThread.Join(TWO_SECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
exception = e;
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private void Host_Closed(object sender, EventArgs e)
|
||||
{
|
||||
logger.Debug("Communication host has been closed.");
|
||||
|
@ -155,5 +237,15 @@ namespace SafeExamBrowser.Core.Communication
|
|||
{
|
||||
logger.Debug($"Communication host has received an unknown message: {e?.Message}.");
|
||||
}
|
||||
|
||||
private string ToString(Message message)
|
||||
{
|
||||
return message != null ? message.ToString() : "<null>";
|
||||
}
|
||||
|
||||
private string ToString(Response response)
|
||||
{
|
||||
return response != null ? response.ToString() : "<null>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace SafeExamBrowser.Core.Communication
|
|||
(channel as ICommunicationObject).Opened += BaseProxy_Opened;
|
||||
(channel as ICommunicationObject).Opening += BaseProxy_Opening;
|
||||
|
||||
Logger.Debug($"Trying to connect to endpoint {address} with authentication token '{token}'...");
|
||||
Logger.Debug($"Trying to connect to endpoint {address}{(token.HasValue ? $" with authentication token '{token}'" : string.Empty)}...");
|
||||
|
||||
var response = channel.Connect(token);
|
||||
|
||||
|
@ -70,21 +70,14 @@ namespace SafeExamBrowser.Core.Communication
|
|||
|
||||
var response = channel.Send(message);
|
||||
|
||||
Logger.Debug($"Sent {ToString(message)}, got {ToString(response)}.");
|
||||
Logger.Debug($"Sent message '{ToString(message)}', got response '{ToString(response)}'.");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected Response Send(MessagePurport purport)
|
||||
protected Response Send(SimpleMessagePurport purport)
|
||||
{
|
||||
FailIfNotConnected(nameof(Send));
|
||||
|
||||
var message = new SimpleMessage { Purport = purport };
|
||||
var response = channel.Send(message);
|
||||
|
||||
Logger.Debug($"Sent {ToString(message)}, got {ToString(response)}.");
|
||||
|
||||
return response;
|
||||
return Send(new SimpleMessage(purport));
|
||||
}
|
||||
|
||||
private void BaseProxy_Closed(object sender, EventArgs e)
|
||||
|
@ -132,12 +125,12 @@ namespace SafeExamBrowser.Core.Communication
|
|||
|
||||
private string ToString(Message message)
|
||||
{
|
||||
return message != null ? $"message of type '{message.GetType()}'" : "no message";
|
||||
return message != null ? message.ToString() : "<null>";
|
||||
}
|
||||
|
||||
private string ToString(Response response)
|
||||
{
|
||||
return response != null ? $"response of type '{response.GetType()}'" : "no response";
|
||||
return response != null ? response.ToString() : "<null>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ namespace SafeExamBrowser.Core.Communication
|
|||
|
||||
public AuthenticationResponse RequestAuthentication()
|
||||
{
|
||||
return (AuthenticationResponse) Send(MessagePurport.ClientIsReady);
|
||||
var response = Send(SimpleMessagePurport.Authenticate);
|
||||
|
||||
return response as AuthenticationResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,12 +22,12 @@ namespace SafeExamBrowser.Core.Communication
|
|||
|
||||
public ClientConfiguration GetConfiguration()
|
||||
{
|
||||
return ((ConfigurationResponse) Send(MessagePurport.ConfigurationNeeded)).Configuration;
|
||||
return ((ConfigurationResponse) Send(SimpleMessagePurport.ConfigurationNeeded)).Configuration;
|
||||
}
|
||||
|
||||
public void InformClientReady()
|
||||
{
|
||||
Send(MessagePurport.ClientIsReady);
|
||||
Send(SimpleMessagePurport.ClientIsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,6 +81,15 @@ namespace SafeExamBrowser.Core.Logging
|
|||
details.AppendLine();
|
||||
details.AppendLine(exception.StackTrace);
|
||||
|
||||
for (var inner = exception.InnerException; inner != null; inner = inner.InnerException)
|
||||
{
|
||||
details.AppendLine();
|
||||
details.AppendLine($" Inner Exception Message: {inner.Message}");
|
||||
details.AppendLine($" Inner Exception Type: {inner.GetType()}");
|
||||
details.AppendLine();
|
||||
details.AppendLine(inner.StackTrace);
|
||||
}
|
||||
|
||||
Add(LogLevel.Error, message);
|
||||
Add(new LogText(details.ToString()));
|
||||
}
|
||||
|
|
|
@ -104,19 +104,19 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
{
|
||||
const int TEN_SECONDS = 10000;
|
||||
|
||||
var clientReady = new AutoResetEvent(false);
|
||||
var clientReadyHandler = new CommunicationEventHandler(() => clientReady.Set());
|
||||
var clientStarted = false;
|
||||
var clientReadyEvent = new AutoResetEvent(false);
|
||||
var clientReadyEventHandler = new CommunicationEventHandler(() => clientReadyEvent.Set());
|
||||
var clientExecutable = configuration.RuntimeInfo.ClientExecutablePath;
|
||||
var clientLogFile = $"{'"' + configuration.RuntimeInfo.ClientLogFile + '"'}";
|
||||
var hostUri = configuration.RuntimeInfo.RuntimeAddress;
|
||||
var token = session.StartupToken.ToString("D");
|
||||
|
||||
runtimeHost.ClientReady += clientReadyHandler;
|
||||
runtimeHost.ClientReady += clientReadyEventHandler;
|
||||
session.ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token);
|
||||
|
||||
var clientStarted = clientReady.WaitOne(TEN_SECONDS);
|
||||
|
||||
runtimeHost.ClientReady -= clientReadyHandler;
|
||||
clientStarted = clientReadyEvent.WaitOne(TEN_SECONDS);
|
||||
runtimeHost.ClientReady -= clientReadyEventHandler;
|
||||
|
||||
// TODO: Check if client process alive!
|
||||
if (clientStarted)
|
||||
|
@ -126,7 +126,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
var response = client.RequestAuthentication();
|
||||
|
||||
// TODO: Further integrity checks necessary?
|
||||
if (session.ClientProcess.Id == response.ProcessId)
|
||||
if (session.ClientProcess.Id == response?.ProcessId)
|
||||
{
|
||||
sessionRunning = true;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Communication.Messages;
|
||||
using SafeExamBrowser.Contracts.Communication.Responses;
|
||||
|
@ -43,21 +42,21 @@ namespace SafeExamBrowser.Runtime.Communication
|
|||
protected override Response OnReceive(Message message)
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
}
|
||||
|
||||
protected override Response OnReceive(MessagePurport message)
|
||||
protected override Response OnReceive(SimpleMessagePurport message)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case MessagePurport.ClientIsReady:
|
||||
case SimpleMessagePurport.ClientIsReady:
|
||||
ClientReady?.Invoke();
|
||||
break;
|
||||
case MessagePurport.ConfigurationNeeded:
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
case SimpleMessagePurport.ConfigurationNeeded:
|
||||
return new ConfigurationResponse { Configuration = configuration.BuildClientConfiguration() };
|
||||
}
|
||||
|
||||
return null;
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace SafeExamBrowser.SystemComponents
|
|||
public class WirelessNetwork : ISystemComponent<ISystemWirelessNetworkControl>
|
||||
{
|
||||
private const int TWO_SECONDS = 2000;
|
||||
private readonly object @lock = new object();
|
||||
|
||||
private ISystemWirelessNetworkControl control;
|
||||
private ILogger logger;
|
||||
|
@ -144,7 +145,7 @@ namespace SafeExamBrowser.SystemComponents
|
|||
|
||||
private void UpdateControl()
|
||||
{
|
||||
lock (networks)
|
||||
lock (@lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Classic"
|
||||
xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
mc:Ignorable="d" Background="White" Foreground="White" Height="500" Width="750" WindowStyle="None" WindowStartupLocation="CenterScreen"
|
||||
mc:Ignorable="d" Background="White" Foreground="White" Height="500" Width="850" WindowStyle="None" WindowStartupLocation="CenterScreen"
|
||||
Icon="./Images/SafeExamBrowser.ico" ResizeMode="NoResize" Title="Safe Exam Browser" Topmost="True">
|
||||
<Grid>
|
||||
<Border x:Name="AnimatedBorder" Panel.ZIndex="10" BorderBrush="DodgerBlue" BorderThickness="5" Visibility="{Binding AnimatedBorderVisibility}">
|
||||
|
@ -36,10 +36,10 @@
|
|||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="350" />
|
||||
<ColumnDefinition Width="400" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Grid.Column="0" Grid.ColumnSpan="2" Margin="-25,0,0,0" Source="pack://application:,,,/SafeExamBrowser.UserInterface.Classic;component/Images/SplashScreen.png" />
|
||||
<TextBlock x:Name="InfoTextBlock" Grid.Column="1" Foreground="Gray" Margin="10,75,175,10" TextWrapping="Wrap" />
|
||||
<TextBlock x:Name="InfoTextBlock" Grid.Column="1" Foreground="Gray" Margin="10,75,225,10" TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
<ProgressBar x:Name="ProgressBar" Grid.Row="1" Background="WhiteSmoke" BorderThickness="0" Foreground="DodgerBlue"
|
||||
IsIndeterminate="{Binding IsIndeterminate}" Maximum="{Binding MaxProgress}" Value="{Binding CurrentProgress}"
|
||||
|
|
Loading…
Reference in a new issue