SEBWIN-219: Extended IPC mechanics with ping mechanism.

This commit is contained in:
dbuechel 2018-02-27 15:28:54 +01:00
parent 268eda9f90
commit 8a06a0fe98
15 changed files with 263 additions and 63 deletions

View file

@ -12,6 +12,7 @@ using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
@ -81,13 +82,21 @@ namespace SafeExamBrowser.Client.Behaviour
var success = operations.TryPerform(); var success = operations.TryPerform();
// TODO
if (success) if (success)
{ {
RegisterEvents(); RegisterEvents();
// TODO: Handle communication exception!
runtime.InformClientReady(); try
{
runtime.InformClientReady();
}
catch (Exception e)
{
logger.Error("Failed to inform runtime that client is ready!", e);
return false;
}
splashScreen.Hide(); splashScreen.Hide();
logger.Info("--- Application successfully initialized ---"); logger.Info("--- Application successfully initialized ---");
@ -110,9 +119,8 @@ namespace SafeExamBrowser.Client.Behaviour
splashScreen.Show(); splashScreen.Show();
splashScreen.BringToForeground(); splashScreen.BringToForeground();
// TODO
DeregisterEvents(); DeregisterEvents();
var success = operations.TryRevert(); var success = operations.TryRevert();
if (success) if (success)
@ -134,6 +142,7 @@ namespace SafeExamBrowser.Client.Behaviour
ClientHost.Shutdown += ClientHost_Shutdown; ClientHost.Shutdown += ClientHost_Shutdown;
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged; displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted; processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
runtime.ConnectionLost += Runtime_ConnectionLost;
taskbar.QuitButtonClicked += Taskbar_QuitButtonClicked; taskbar.QuitButtonClicked += Taskbar_QuitButtonClicked;
windowMonitor.WindowChanged += WindowMonitor_WindowChanged; windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
} }
@ -143,6 +152,7 @@ namespace SafeExamBrowser.Client.Behaviour
ClientHost.Shutdown -= ClientHost_Shutdown; ClientHost.Shutdown -= ClientHost_Shutdown;
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged; displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted; processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
runtime.ConnectionLost -= Runtime_ConnectionLost;
taskbar.QuitButtonClicked -= Taskbar_QuitButtonClicked; taskbar.QuitButtonClicked -= Taskbar_QuitButtonClicked;
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged; windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
} }
@ -173,11 +183,31 @@ namespace SafeExamBrowser.Client.Behaviour
shutdown.Invoke(); shutdown.Invoke();
} }
private void Runtime_ConnectionLost()
{
logger.Error("Lost connection to the runtime!");
uiFactory.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
taskbar.Close();
shutdown.Invoke();
}
private void Taskbar_QuitButtonClicked() private void Taskbar_QuitButtonClicked()
{ {
// TODO: MessageBox asking whether user really wants to quit -> only then request shutdown! var result = uiFactory.Show(TextKey.MessageBox_Quit, TextKey.MessageBox_QuitTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question);
// TODO: Handle communication exception!
runtime.RequestShutdown(); if (result == MessageBoxResult.Yes)
{
try
{
runtime.RequestShutdown();
}
catch (Exception e)
{
logger.Error("Failed to communicate shutdown request to the runtime!", e);
uiFactory.Show(TextKey.MessageBox_QuitError, TextKey.MessageBox_QuitErrorTitle, icon: MessageBoxIcon.Error);
}
}
} }
private void WindowMonitor_WindowChanged(IntPtr window) private void WindowMonitor_WindowChanged(IntPtr window)

View file

@ -17,6 +17,7 @@ namespace SafeExamBrowser.Client.Communication
{ {
internal class ClientHost : BaseHost, IClientHost internal class ClientHost : BaseHost, IClientHost
{ {
private bool allowConnection = true;
private int processId; private int processId;
public Guid StartupToken { private get; set; } public Guid StartupToken { private get; set; }
@ -30,18 +31,24 @@ namespace SafeExamBrowser.Client.Communication
protected override bool OnConnect(Guid? token) protected override bool OnConnect(Guid? token)
{ {
return StartupToken == token; var authenticated = StartupToken == token;
var accepted = allowConnection && authenticated;
if (accepted)
{
allowConnection = false;
}
return accepted;
} }
protected override void OnDisconnect() protected override void OnDisconnect()
{ {
// TODO // Nothing to do here...
} }
protected override Response OnReceive(Message message) protected override Response OnReceive(Message message)
{ {
// TODO
return new SimpleResponse(SimpleResponsePurport.UnknownMessage); return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
} }

View file

@ -13,7 +13,13 @@ namespace SafeExamBrowser.Contracts.Communication
public interface ICommunicationProxy public interface ICommunicationProxy
{ {
/// <summary> /// <summary>
/// Tries to establish a connection. Returns <c>true</c> if the connection has been accepted, otherwise <c>false</c>. /// Fired when the connection to the proxy was lost, e.g. if a ping request failed or a communication fault occurred.
/// </summary>
event CommunicationEventHandler ConnectionLost;
/// <summary>
/// Tries to establish a connection. Returns <c>true</c> if the connection has been accepted, otherwise <c>false</c>. If a
/// connection was successfully established, a ping mechanism will be activated to periodically check the connection status.
/// </summary> /// </summary>
/// <exception cref="System.ServiceModel.*">If the communication failed.</exception> /// <exception cref="System.ServiceModel.*">If the communication failed.</exception>
bool Connect(Guid? token = null); bool Connect(Guid? token = null);

View file

@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Communication.Messages namespace SafeExamBrowser.Contracts.Communication.Messages
{ {
[Serializable] [Serializable]
public class Message public abstract class Message
{ {
/// <summary> /// <summary>
/// The communication token needed for authentication. /// The communication token needed for authentication.

View file

@ -28,6 +28,11 @@ namespace SafeExamBrowser.Contracts.Communication.Messages
/// </summary> /// </summary>
ConfigurationNeeded, ConfigurationNeeded,
/// <summary>
/// Requests an interlocutor to signal that the connection status is okay.
/// </summary>
Ping,
/// <summary> /// <summary>
/// Sent from the client to the runtime to request shutting down the application. /// Sent from the client to the runtime to request shutting down the application.
/// </summary> /// </summary>

View file

@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Communication.Responses namespace SafeExamBrowser.Contracts.Communication.Responses
{ {
[Serializable] [Serializable]
public class Response public abstract class Response
{ {
public override string ToString() public override string ToString()
{ {

View file

@ -15,10 +15,18 @@ namespace SafeExamBrowser.Contracts.I18n
{ {
Browser_ShowDeveloperConsole, Browser_ShowDeveloperConsole,
LogWindow_Title, LogWindow_Title,
MessageBox_ApplicationError,
MessageBox_ApplicationErrorTitle,
MessageBox_ConfigureClientSuccess, MessageBox_ConfigureClientSuccess,
MessageBox_ConfigureClientSuccessTitle, MessageBox_ConfigureClientSuccessTitle,
MessageBox_Quit,
MessageBox_QuitTitle,
MessageBox_QuitError,
MessageBox_QuitErrorTitle,
MessageBox_SessionStartError, MessageBox_SessionStartError,
MessageBox_SessionStartErrorTitle, MessageBox_SessionStartErrorTitle,
MessageBox_SessionStopError,
MessageBox_SessionStopErrorTitle,
MessageBox_ShutdownError, MessageBox_ShutdownError,
MessageBox_ShutdownErrorTitle, MessageBox_ShutdownErrorTitle,
MessageBox_SingleInstance, MessageBox_SingleInstance,

View file

@ -101,13 +101,17 @@ namespace SafeExamBrowser.Core.Communication
if (IsAuthorized(message?.CommunicationToken)) if (IsAuthorized(message?.CommunicationToken))
{ {
if (message is SimpleMessage simpleMessage) switch (message)
{ {
response = OnReceive(simpleMessage.Purport); case SimpleMessage simpleMessage when simpleMessage.Purport == SimpleMessagePurport.Ping:
} response = new SimpleResponse(SimpleResponsePurport.Acknowledged);
else break;
{ case SimpleMessage simpleMessage:
response = OnReceive(message); response = OnReceive(simpleMessage.Purport);
break;
default:
response = OnReceive(message);
break;
} }
} }
@ -218,7 +222,7 @@ namespace SafeExamBrowser.Core.Communication
private void Host_Faulted(object sender, EventArgs e) private void Host_Faulted(object sender, EventArgs e)
{ {
logger.Debug("Communication host has faulted!"); logger.Error("Communication host has faulted!");
} }
private void Host_Opened(object sender, EventArgs e) private void Host_Opened(object sender, EventArgs e)
@ -233,7 +237,7 @@ namespace SafeExamBrowser.Core.Communication
private void Host_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e) private void Host_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e)
{ {
logger.Debug($"Communication host has received an unknown message: {e?.Message}."); logger.Warn($"Communication host has received an unknown message: {e?.Message}.");
} }
private string ToString(Message message) private string ToString(Message message)

View file

@ -8,6 +8,7 @@
using System; using System;
using System.ServiceModel; using System.ServiceModel;
using System.Timers;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Messages; using SafeExamBrowser.Contracts.Communication.Messages;
using SafeExamBrowser.Contracts.Communication.Responses; using SafeExamBrowser.Contracts.Communication.Responses;
@ -17,12 +18,18 @@ namespace SafeExamBrowser.Core.Communication
{ {
public abstract class BaseProxy : ICommunicationProxy public abstract class BaseProxy : ICommunicationProxy
{ {
private const int ONE_MINUTE = 60000;
private static readonly object @lock = new object();
private string address; private string address;
private ICommunication channel; private ICommunication channel;
private Guid? communicationToken; private Guid? communicationToken;
private Timer timer;
protected ILogger Logger { get; private set; } protected ILogger Logger { get; private set; }
public event CommunicationEventHandler ConnectionLost;
public BaseProxy(string address, ILogger logger) public BaseProxy(string address, ILogger logger)
{ {
this.address = address; this.address = address;
@ -47,12 +54,18 @@ namespace SafeExamBrowser.Core.Communication
communicationToken = response.CommunicationToken; communicationToken = response.CommunicationToken;
Logger.Debug($"Connection was {(response.ConnectionEstablished ? "established" : "refused")}."); Logger.Debug($"Connection was {(response.ConnectionEstablished ? "established" : "refused")}.");
if (response.ConnectionEstablished)
{
StartAutoPing();
}
return response.ConnectionEstablished; return response.ConnectionEstablished;
} }
public virtual bool Disconnect() public virtual bool Disconnect()
{ {
FailIfNotConnected(nameof(Disconnect)); FailIfNotConnected(nameof(Disconnect));
StopAutoPing();
var message = new DisconnectionMessage { CommunicationToken = communicationToken.Value }; var message = new DisconnectionMessage { CommunicationToken = communicationToken.Value };
var response = channel.Disconnect(message); var response = channel.Disconnect(message);
@ -80,6 +93,11 @@ namespace SafeExamBrowser.Core.Communication
return Send(new SimpleMessage(purport)); return Send(new SimpleMessage(purport));
} }
protected bool IsAcknowledged(Response response)
{
return response is SimpleResponse simpleResponse && simpleResponse.Purport == SimpleResponsePurport.Acknowledged;
}
protected string ToString(Message message) protected string ToString(Message message)
{ {
return message != null ? message.ToString() : "<null>"; return message != null ? message.ToString() : "<null>";
@ -102,7 +120,8 @@ namespace SafeExamBrowser.Core.Communication
private void BaseProxy_Faulted(object sender, EventArgs e) private void BaseProxy_Faulted(object sender, EventArgs e)
{ {
Logger.Debug("Communication channel has faulted!"); Logger.Error("Communication channel has faulted!");
ConnectionLost?.Invoke();
} }
private void BaseProxy_Opened(object sender, EventArgs e) private void BaseProxy_Opened(object sender, EventArgs e)
@ -132,5 +151,54 @@ namespace SafeExamBrowser.Core.Communication
{ {
return channel == null ? "null" : $"in state '{(channel as ICommunicationObject).State}'"; return channel == null ? "null" : $"in state '{(channel as ICommunicationObject).State}'";
} }
private void StartAutoPing()
{
lock (@lock)
{
timer = new Timer(ONE_MINUTE) { AutoReset = true };
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
}
private void StopAutoPing()
{
lock (@lock)
{
timer?.Stop();
}
}
private void Timer_Elapsed(object sender, ElapsedEventArgs args)
{
lock (@lock)
{
if (timer.Enabled)
{
try
{
var response = Send(SimpleMessagePurport.Ping);
if (IsAcknowledged(response))
{
Logger.Info("Pinged proxy, connection is alive.");
}
else
{
Logger.Error($"Proxy did not acknowledge ping message! Received: {ToString(response)}.");
timer.Stop();
ConnectionLost?.Invoke();
}
}
catch (Exception e)
{
Logger.Error("Failed to ping proxy!", e);
timer.Stop();
ConnectionLost?.Invoke();
}
}
}
}
} }
} }

View file

@ -6,6 +6,7 @@
* 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.ServiceModel;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Messages; using SafeExamBrowser.Contracts.Communication.Messages;
using SafeExamBrowser.Contracts.Communication.Responses; using SafeExamBrowser.Contracts.Communication.Responses;
@ -23,9 +24,9 @@ namespace SafeExamBrowser.Core.Communication
{ {
var response = Send(SimpleMessagePurport.Shutdown); var response = Send(SimpleMessagePurport.Shutdown);
if (response is SimpleResponse simpleMessage && simpleMessage.Purport == SimpleResponsePurport.Acknowledged) if (!IsAcknowledged(response))
{ {
// TODO throw new CommunicationException($"Runtime did not acknowledge shutdown request! Received: {ToString(response)}.");
} }
} }
@ -33,7 +34,12 @@ namespace SafeExamBrowser.Core.Communication
{ {
var response = Send(SimpleMessagePurport.Authenticate); var response = Send(SimpleMessagePurport.Authenticate);
return response as AuthenticationResponse; if (response is AuthenticationResponse authenticationResponse)
{
return authenticationResponse;
}
throw new CommunicationException($"Did not receive authentication response! Received: {ToString(response)}.");
} }
} }
} }

View file

@ -52,10 +52,5 @@ namespace SafeExamBrowser.Core.Communication
throw new CommunicationException($"Runtime did not acknowledge shutdown request! Response: {ToString(response)}."); throw new CommunicationException($"Runtime did not acknowledge shutdown request! Response: {ToString(response)}.");
} }
} }
private bool IsAcknowledged(Response response)
{
return response is SimpleResponse simpleResponse && simpleResponse.Purport == SimpleResponsePurport.Acknowledged;
}
} }
} }

View file

@ -6,18 +6,42 @@
<Entry key="LogWindow_Title"> <Entry key="LogWindow_Title">
Application Log Application Log
</Entry> </Entry>
<Entry key="MessageBox_ApplicationError">
An unrecoverable error has occurred! Please consult the application log for more information. The application will now shut down...
</Entry>
<Entry key="MessageBox_ApplicationErrorTitle">
Application Error
</Entry>
<Entry key="MessageBox_ConfigureClientSuccess"> <Entry key="MessageBox_ConfigureClientSuccess">
The client configuration has been saved and will be used when you start the application the next time. Do you want to quit for now? The client configuration has been saved and will be used when you start the application the next time. Do you want to quit for now?
</Entry> </Entry>
<Entry key="MessageBox_ConfigureClientSuccessTitle"> <Entry key="MessageBox_ConfigureClientSuccessTitle">
Configuration Successful Configuration Successful
</Entry> </Entry>
<Entry key="MessageBox_Quit">
Would you really like to quit the application?
</Entry>
<Entry key="MessageBox_QuitTitle">
Quit?
</Entry>
<Entry key="MessageBox_QuitError">
The client failed to communicate the shutdown request to the runtime! Please try again...
</Entry>
<Entry key="MessageBox_QuitErrorTitle">
Quit Error
</Entry>
<Entry key="MessageBox_SessionStartError"> <Entry key="MessageBox_SessionStartError">
The application failed to start a new session. Please consult the application log for more information... The application failed to start a new session. Please consult the application log for more information...
</Entry> </Entry>
<Entry key="MessageBox_SessionStartErrorTitle"> <Entry key="MessageBox_SessionStartErrorTitle">
Session Start Error Session Start Error
</Entry> </Entry>
<Entry key="MessageBox_SessionStopError">
The application failed to properly stop the running session. Please consult the application log for more information...
</Entry>
<Entry key="MessageBox_SessionStopErrorTitle">
Session Stop Error
</Entry>
<Entry key="MessageBox_ShutdownError"> <Entry key="MessageBox_ShutdownError">
An unexpected error occurred during the shutdown procedure! Please consult the application log for more information... An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...
</Entry> </Entry>

View file

@ -6,7 +6,6 @@
* 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;
using System.Threading; using System.Threading;
using SafeExamBrowser.Contracts.Behaviour.Operations; using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
@ -65,14 +64,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
logger.Info("Initializing service session..."); logger.Info("Initializing service session...");
service.StartSession(session.Id, configuration.CurrentSettings); service.StartSession(session.Id, configuration.CurrentSettings);
try sessionRunning = TryStartClient();
{
sessionRunning = TryStartClient();
}
catch (Exception e)
{
logger.Error("Failed to start client!", e);
}
if (sessionRunning) if (sessionRunning)
{ {
@ -96,14 +88,10 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
logger.Info("Stopping service session..."); logger.Info("Stopping service session...");
service.StopSession(session.Id); service.StopSession(session.Id);
try if (!session.ClientProcess.HasTerminated)
{ {
StopClient(); StopClient();
} }
catch (Exception e)
{
logger.Error("Failed to terminate client!", e);
}
sessionRunning = false; sessionRunning = false;
logger.Info($"Successfully stopped session with identifier '{session.Id}'."); logger.Info($"Successfully stopped session with identifier '{session.Id}'.");
@ -204,7 +192,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
} }
else else
{ {
logger.Warn("Attempting to kill client process since graceful shutdown failed!"); logger.Warn("Attempting to kill client process since graceful termination failed!");
KillClient(); KillClient();
} }
} }

View file

@ -75,12 +75,12 @@ namespace SafeExamBrowser.Runtime.Behaviour
if (initialized) if (initialized)
{ {
RegisterEvents();
logger.Info("--- Application successfully initialized ---"); logger.Info("--- Application successfully initialized ---");
logger.Log(string.Empty); logger.Log(string.Empty);
logger.Subscribe(runtimeWindow); logger.Subscribe(runtimeWindow);
splashScreen.Hide(); splashScreen.Hide();
runtimeHost.ShutdownRequested += RuntimeHost_ShutdownRequested;
StartSession(true); StartSession(true);
} }
@ -88,6 +88,8 @@ namespace SafeExamBrowser.Runtime.Behaviour
{ {
logger.Info("--- Application startup aborted! ---"); logger.Info("--- Application startup aborted! ---");
logger.Log(string.Empty); logger.Log(string.Empty);
uiFactory.Show(TextKey.MessageBox_StartupError, TextKey.MessageBox_StartupErrorTitle, icon: MessageBoxIcon.Error);
} }
return initialized && sessionRunning; return initialized && sessionRunning;
@ -95,8 +97,11 @@ namespace SafeExamBrowser.Runtime.Behaviour
public void Terminate() public void Terminate()
{ {
DeregisterEvents();
if (sessionRunning) if (sessionRunning)
{ {
DeregisterSessionEvents();
StopSession(); StopSession();
} }
@ -119,6 +124,8 @@ namespace SafeExamBrowser.Runtime.Behaviour
{ {
logger.Info("--- Shutdown procedure failed! ---"); logger.Info("--- Shutdown procedure failed! ---");
logger.Log(string.Empty); logger.Log(string.Empty);
uiFactory.Show(TextKey.MessageBox_ShutdownError, TextKey.MessageBox_ShutdownErrorTitle, icon: MessageBoxIcon.Error);
} }
splashScreen?.Close(); splashScreen?.Close();
@ -127,13 +134,20 @@ namespace SafeExamBrowser.Runtime.Behaviour
private void StartSession(bool initial = false) private void StartSession(bool initial = false)
{ {
runtimeWindow.Show(); runtimeWindow.Show();
logger.Info(">------ Initiating session procedure ------<"); logger.Info(">>>--- Initiating session procedure ---<<<");
if (sessionRunning)
{
DeregisterSessionEvents();
}
sessionRunning = initial ? sessionSequence.TryPerform() : sessionSequence.TryRepeat(); sessionRunning = initial ? sessionSequence.TryPerform() : sessionSequence.TryRepeat();
if (sessionRunning) if (sessionRunning)
{ {
logger.Info(">------ Session is running ------<"); RegisterSessionEvents();
logger.Info(">>>--- Session is running ---<<<");
runtimeWindow.HideProgressBar(); runtimeWindow.HideProgressBar();
runtimeWindow.UpdateText(TextKey.RuntimeWindow_ApplicationRunning); runtimeWindow.UpdateText(TextKey.RuntimeWindow_ApplicationRunning);
@ -144,7 +158,7 @@ namespace SafeExamBrowser.Runtime.Behaviour
} }
else else
{ {
logger.Info(">------ Session procedure was aborted! ------<"); logger.Info(">>>--- Session procedure was aborted! ---<<<");
// TODO: Not when user chose to terminate after reconfiguration! Probably needs IOperationSequenceResult or alike... // TODO: Not when user chose to terminate after reconfiguration! Probably needs IOperationSequenceResult or alike...
uiFactory.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error); uiFactory.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error);
@ -160,28 +174,65 @@ namespace SafeExamBrowser.Runtime.Behaviour
runtimeWindow.Show(); runtimeWindow.Show();
runtimeWindow.BringToForeground(); runtimeWindow.BringToForeground();
runtimeWindow.ShowProgressBar(); runtimeWindow.ShowProgressBar();
logger.Info(">------ Reverting session operations ------<"); logger.Info(">>>--- Reverting session operations ---<<<");
var success = sessionSequence.TryRevert(); var success = sessionSequence.TryRevert();
if (success) if (success)
{ {
logger.Info(">------ Session is terminated ------<"); logger.Info(">>>--- Session is terminated ---<<<");
sessionRunning = false; sessionRunning = false;
// TODO
} }
else else
{ {
logger.Info(">------ Session reversion was erroneous! ------<"); logger.Info(">>>--- Session reversion was erroneous! ---<<<");
uiFactory.Show(TextKey.MessageBox_SessionStopError, TextKey.MessageBox_SessionStopErrorTitle, icon: MessageBoxIcon.Error);
// TODO
} }
} }
private void RegisterEvents()
{
client.ConnectionLost += Client_ConnectionLost;
runtimeHost.ShutdownRequested += RuntimeHost_ShutdownRequested;
}
private void RegisterSessionEvents()
{
configuration.CurrentSession.ClientProcess.Terminated += ClientProcess_Terminated;
}
private void DeregisterEvents()
{
client.ConnectionLost -= Client_ConnectionLost;
runtimeHost.ShutdownRequested -= RuntimeHost_ShutdownRequested;
}
private void DeregisterSessionEvents()
{
configuration.CurrentSession.ClientProcess.Terminated -= ClientProcess_Terminated;
}
private void ClientProcess_Terminated(int exitCode)
{
logger.Error($"Client application has unexpectedly terminated with exit code {exitCode}!");
// TODO: Check if message box is rendered on new desktop as well -> otherwise shutdown is blocked!
uiFactory.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
shutdown.Invoke();
}
private void Client_ConnectionLost()
{
logger.Error("Lost connection to the client application!");
// TODO: Check if message box is rendered on new desktop as well -> otherwise shutdown is blocked!
uiFactory.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
shutdown.Invoke();
}
private void RuntimeHost_ShutdownRequested() private void RuntimeHost_ShutdownRequested()
{ {
logger.Info("Received shutdown request from client application."); logger.Info("Received shutdown request from the client application.");
shutdown.Invoke(); shutdown.Invoke();
} }
} }

View file

@ -18,6 +18,7 @@ namespace SafeExamBrowser.Runtime.Communication
{ {
internal class RuntimeHost : BaseHost, IRuntimeHost internal class RuntimeHost : BaseHost, IRuntimeHost
{ {
private bool allowConnection = true;
private IConfigurationRepository configuration; private IConfigurationRepository configuration;
public Guid StartupToken { private get; set; } public Guid StartupToken { private get; set; }
@ -33,7 +34,15 @@ namespace SafeExamBrowser.Runtime.Communication
protected override bool OnConnect(Guid? token = null) protected override bool OnConnect(Guid? token = null)
{ {
return StartupToken == token; var authenticated = StartupToken == token;
var accepted = allowConnection && authenticated;
if (accepted)
{
allowConnection = false;
}
return accepted;
} }
protected override void OnDisconnect() protected override void OnDisconnect()
@ -43,7 +52,6 @@ namespace SafeExamBrowser.Runtime.Communication
protected override Response OnReceive(Message message) protected override Response OnReceive(Message message)
{ {
// TODO
return new SimpleResponse(SimpleResponsePurport.UnknownMessage); return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
} }