diff --git a/SafeExamBrowser.Client/Behaviour/ClientController.cs b/SafeExamBrowser.Client/Behaviour/ClientController.cs
index 81f35621..846a6d5b 100644
--- a/SafeExamBrowser.Client/Behaviour/ClientController.cs
+++ b/SafeExamBrowser.Client/Behaviour/ClientController.cs
@@ -12,6 +12,7 @@ using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
+using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
@@ -81,13 +82,21 @@ namespace SafeExamBrowser.Client.Behaviour
var success = operations.TryPerform();
- // TODO
-
if (success)
{
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();
logger.Info("--- Application successfully initialized ---");
@@ -110,9 +119,8 @@ namespace SafeExamBrowser.Client.Behaviour
splashScreen.Show();
splashScreen.BringToForeground();
- // TODO
-
DeregisterEvents();
+
var success = operations.TryRevert();
if (success)
@@ -134,6 +142,7 @@ namespace SafeExamBrowser.Client.Behaviour
ClientHost.Shutdown += ClientHost_Shutdown;
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
+ runtime.ConnectionLost += Runtime_ConnectionLost;
taskbar.QuitButtonClicked += Taskbar_QuitButtonClicked;
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
}
@@ -143,6 +152,7 @@ namespace SafeExamBrowser.Client.Behaviour
ClientHost.Shutdown -= ClientHost_Shutdown;
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
+ runtime.ConnectionLost -= Runtime_ConnectionLost;
taskbar.QuitButtonClicked -= Taskbar_QuitButtonClicked;
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
}
@@ -173,11 +183,31 @@ namespace SafeExamBrowser.Client.Behaviour
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()
{
- // TODO: MessageBox asking whether user really wants to quit -> only then request shutdown!
- // TODO: Handle communication exception!
- runtime.RequestShutdown();
+ var result = uiFactory.Show(TextKey.MessageBox_Quit, TextKey.MessageBox_QuitTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question);
+
+ 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)
diff --git a/SafeExamBrowser.Client/Communication/ClientHost.cs b/SafeExamBrowser.Client/Communication/ClientHost.cs
index fdcbedf2..664858f7 100644
--- a/SafeExamBrowser.Client/Communication/ClientHost.cs
+++ b/SafeExamBrowser.Client/Communication/ClientHost.cs
@@ -17,6 +17,7 @@ namespace SafeExamBrowser.Client.Communication
{
internal class ClientHost : BaseHost, IClientHost
{
+ private bool allowConnection = true;
private int processId;
public Guid StartupToken { private get; set; }
@@ -30,18 +31,24 @@ namespace SafeExamBrowser.Client.Communication
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()
{
- // TODO
+ // Nothing to do here...
}
protected override Response OnReceive(Message message)
{
- // TODO
-
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
}
diff --git a/SafeExamBrowser.Contracts/Communication/ICommunicationProxy.cs b/SafeExamBrowser.Contracts/Communication/ICommunicationProxy.cs
index d69e99bb..58484637 100644
--- a/SafeExamBrowser.Contracts/Communication/ICommunicationProxy.cs
+++ b/SafeExamBrowser.Contracts/Communication/ICommunicationProxy.cs
@@ -13,7 +13,13 @@ namespace SafeExamBrowser.Contracts.Communication
public interface ICommunicationProxy
{
///
- /// Tries to establish a connection. Returns true if the connection has been accepted, otherwise false.
+ /// Fired when the connection to the proxy was lost, e.g. if a ping request failed or a communication fault occurred.
+ ///
+ event CommunicationEventHandler ConnectionLost;
+
+ ///
+ /// Tries to establish a connection. Returns true if the connection has been accepted, otherwise false. If a
+ /// connection was successfully established, a ping mechanism will be activated to periodically check the connection status.
///
/// If the communication failed.
bool Connect(Guid? token = null);
diff --git a/SafeExamBrowser.Contracts/Communication/Messages/Message.cs b/SafeExamBrowser.Contracts/Communication/Messages/Message.cs
index 54f8700e..caa850ae 100644
--- a/SafeExamBrowser.Contracts/Communication/Messages/Message.cs
+++ b/SafeExamBrowser.Contracts/Communication/Messages/Message.cs
@@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Communication.Messages
{
[Serializable]
- public class Message
+ public abstract class Message
{
///
/// The communication token needed for authentication.
diff --git a/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessagePurport.cs b/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessagePurport.cs
index 79a266ec..e6f1d3f5 100644
--- a/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessagePurport.cs
+++ b/SafeExamBrowser.Contracts/Communication/Messages/SimpleMessagePurport.cs
@@ -28,6 +28,11 @@ namespace SafeExamBrowser.Contracts.Communication.Messages
///
ConfigurationNeeded,
+ ///
+ /// Requests an interlocutor to signal that the connection status is okay.
+ ///
+ Ping,
+
///
/// Sent from the client to the runtime to request shutting down the application.
///
diff --git a/SafeExamBrowser.Contracts/Communication/Responses/Response.cs b/SafeExamBrowser.Contracts/Communication/Responses/Response.cs
index ba3f061d..0c91d224 100644
--- a/SafeExamBrowser.Contracts/Communication/Responses/Response.cs
+++ b/SafeExamBrowser.Contracts/Communication/Responses/Response.cs
@@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Communication.Responses
{
[Serializable]
- public class Response
+ public abstract class Response
{
public override string ToString()
{
diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs
index 65e43f21..f7615c03 100644
--- a/SafeExamBrowser.Contracts/I18n/TextKey.cs
+++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs
@@ -15,10 +15,18 @@ namespace SafeExamBrowser.Contracts.I18n
{
Browser_ShowDeveloperConsole,
LogWindow_Title,
+ MessageBox_ApplicationError,
+ MessageBox_ApplicationErrorTitle,
MessageBox_ConfigureClientSuccess,
MessageBox_ConfigureClientSuccessTitle,
+ MessageBox_Quit,
+ MessageBox_QuitTitle,
+ MessageBox_QuitError,
+ MessageBox_QuitErrorTitle,
MessageBox_SessionStartError,
MessageBox_SessionStartErrorTitle,
+ MessageBox_SessionStopError,
+ MessageBox_SessionStopErrorTitle,
MessageBox_ShutdownError,
MessageBox_ShutdownErrorTitle,
MessageBox_SingleInstance,
diff --git a/SafeExamBrowser.Core/Communication/BaseHost.cs b/SafeExamBrowser.Core/Communication/BaseHost.cs
index 3993bfa6..02fffde0 100644
--- a/SafeExamBrowser.Core/Communication/BaseHost.cs
+++ b/SafeExamBrowser.Core/Communication/BaseHost.cs
@@ -101,13 +101,17 @@ namespace SafeExamBrowser.Core.Communication
if (IsAuthorized(message?.CommunicationToken))
{
- if (message is SimpleMessage simpleMessage)
+ switch (message)
{
- response = OnReceive(simpleMessage.Purport);
- }
- else
- {
- response = OnReceive(message);
+ case SimpleMessage simpleMessage when simpleMessage.Purport == SimpleMessagePurport.Ping:
+ response = new SimpleResponse(SimpleResponsePurport.Acknowledged);
+ break;
+ case SimpleMessage simpleMessage:
+ 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)
{
- logger.Debug("Communication host has faulted!");
+ logger.Error("Communication host has faulted!");
}
private void Host_Opened(object sender, EventArgs e)
@@ -233,7 +237,7 @@ namespace SafeExamBrowser.Core.Communication
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)
diff --git a/SafeExamBrowser.Core/Communication/BaseProxy.cs b/SafeExamBrowser.Core/Communication/BaseProxy.cs
index 40ef9772..321716a5 100644
--- a/SafeExamBrowser.Core/Communication/BaseProxy.cs
+++ b/SafeExamBrowser.Core/Communication/BaseProxy.cs
@@ -8,6 +8,7 @@
using System;
using System.ServiceModel;
+using System.Timers;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Messages;
using SafeExamBrowser.Contracts.Communication.Responses;
@@ -17,12 +18,18 @@ namespace SafeExamBrowser.Core.Communication
{
public abstract class BaseProxy : ICommunicationProxy
{
+ private const int ONE_MINUTE = 60000;
+ private static readonly object @lock = new object();
+
private string address;
private ICommunication channel;
private Guid? communicationToken;
+ private Timer timer;
protected ILogger Logger { get; private set; }
+ public event CommunicationEventHandler ConnectionLost;
+
public BaseProxy(string address, ILogger logger)
{
this.address = address;
@@ -47,12 +54,18 @@ namespace SafeExamBrowser.Core.Communication
communicationToken = response.CommunicationToken;
Logger.Debug($"Connection was {(response.ConnectionEstablished ? "established" : "refused")}.");
+ if (response.ConnectionEstablished)
+ {
+ StartAutoPing();
+ }
+
return response.ConnectionEstablished;
}
public virtual bool Disconnect()
{
FailIfNotConnected(nameof(Disconnect));
+ StopAutoPing();
var message = new DisconnectionMessage { CommunicationToken = communicationToken.Value };
var response = channel.Disconnect(message);
@@ -80,6 +93,11 @@ namespace SafeExamBrowser.Core.Communication
return Send(new SimpleMessage(purport));
}
+ protected bool IsAcknowledged(Response response)
+ {
+ return response is SimpleResponse simpleResponse && simpleResponse.Purport == SimpleResponsePurport.Acknowledged;
+ }
+
protected string ToString(Message message)
{
return message != null ? message.ToString() : "";
@@ -102,7 +120,8 @@ namespace SafeExamBrowser.Core.Communication
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)
@@ -132,5 +151,54 @@ namespace SafeExamBrowser.Core.Communication
{
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();
+ }
+ }
+ }
+ }
}
}
diff --git a/SafeExamBrowser.Core/Communication/ClientProxy.cs b/SafeExamBrowser.Core/Communication/ClientProxy.cs
index 5009463b..f10978bc 100644
--- a/SafeExamBrowser.Core/Communication/ClientProxy.cs
+++ b/SafeExamBrowser.Core/Communication/ClientProxy.cs
@@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System.ServiceModel;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Messages;
using SafeExamBrowser.Contracts.Communication.Responses;
@@ -23,9 +24,9 @@ namespace SafeExamBrowser.Core.Communication
{
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);
- return response as AuthenticationResponse;
+ if (response is AuthenticationResponse authenticationResponse)
+ {
+ return authenticationResponse;
+ }
+
+ throw new CommunicationException($"Did not receive authentication response! Received: {ToString(response)}.");
}
}
}
diff --git a/SafeExamBrowser.Core/Communication/RuntimeProxy.cs b/SafeExamBrowser.Core/Communication/RuntimeProxy.cs
index 39d4fb0f..ef0860f3 100644
--- a/SafeExamBrowser.Core/Communication/RuntimeProxy.cs
+++ b/SafeExamBrowser.Core/Communication/RuntimeProxy.cs
@@ -52,10 +52,5 @@ namespace SafeExamBrowser.Core.Communication
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;
- }
}
}
diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml
index a370bd33..3c42d27b 100644
--- a/SafeExamBrowser.Core/I18n/Text.xml
+++ b/SafeExamBrowser.Core/I18n/Text.xml
@@ -6,18 +6,42 @@
Application Log
+
+ An unrecoverable error has occurred! Please consult the application log for more information. The application will now shut down...
+
+
+ Application Error
+
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?
Configuration Successful
+
+ Would you really like to quit the application?
+
+
+ Quit?
+
+
+ The client failed to communicate the shutdown request to the runtime! Please try again...
+
+
+ Quit Error
+
The application failed to start a new session. Please consult the application log for more information...
Session Start Error
+
+ The application failed to properly stop the running session. Please consult the application log for more information...
+
+
+ Session Stop Error
+
An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...
diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs
index 827df9f9..86d76471 100644
--- a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs
+++ b/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs
@@ -6,7 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-using System;
using System.Threading;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication;
@@ -65,14 +64,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
logger.Info("Initializing service session...");
service.StartSession(session.Id, configuration.CurrentSettings);
- try
- {
- sessionRunning = TryStartClient();
- }
- catch (Exception e)
- {
- logger.Error("Failed to start client!", e);
- }
+ sessionRunning = TryStartClient();
if (sessionRunning)
{
@@ -96,14 +88,10 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
logger.Info("Stopping service session...");
service.StopSession(session.Id);
- try
+ if (!session.ClientProcess.HasTerminated)
{
StopClient();
}
- catch (Exception e)
- {
- logger.Error("Failed to terminate client!", e);
- }
sessionRunning = false;
logger.Info($"Successfully stopped session with identifier '{session.Id}'.");
@@ -204,7 +192,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
}
else
{
- logger.Warn("Attempting to kill client process since graceful shutdown failed!");
+ logger.Warn("Attempting to kill client process since graceful termination failed!");
KillClient();
}
}
diff --git a/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs b/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs
index ab3ff9a5..e23a779a 100644
--- a/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs
+++ b/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs
@@ -75,12 +75,12 @@ namespace SafeExamBrowser.Runtime.Behaviour
if (initialized)
{
+ RegisterEvents();
+
logger.Info("--- Application successfully initialized ---");
logger.Log(string.Empty);
logger.Subscribe(runtimeWindow);
-
splashScreen.Hide();
- runtimeHost.ShutdownRequested += RuntimeHost_ShutdownRequested;
StartSession(true);
}
@@ -88,6 +88,8 @@ namespace SafeExamBrowser.Runtime.Behaviour
{
logger.Info("--- Application startup aborted! ---");
logger.Log(string.Empty);
+
+ uiFactory.Show(TextKey.MessageBox_StartupError, TextKey.MessageBox_StartupErrorTitle, icon: MessageBoxIcon.Error);
}
return initialized && sessionRunning;
@@ -95,8 +97,11 @@ namespace SafeExamBrowser.Runtime.Behaviour
public void Terminate()
{
+ DeregisterEvents();
+
if (sessionRunning)
{
+ DeregisterSessionEvents();
StopSession();
}
@@ -119,6 +124,8 @@ namespace SafeExamBrowser.Runtime.Behaviour
{
logger.Info("--- Shutdown procedure failed! ---");
logger.Log(string.Empty);
+
+ uiFactory.Show(TextKey.MessageBox_ShutdownError, TextKey.MessageBox_ShutdownErrorTitle, icon: MessageBoxIcon.Error);
}
splashScreen?.Close();
@@ -127,13 +134,20 @@ namespace SafeExamBrowser.Runtime.Behaviour
private void StartSession(bool initial = false)
{
runtimeWindow.Show();
- logger.Info(">------ Initiating session procedure ------<");
+ logger.Info(">>>--- Initiating session procedure ---<<<");
+
+ if (sessionRunning)
+ {
+ DeregisterSessionEvents();
+ }
sessionRunning = initial ? sessionSequence.TryPerform() : sessionSequence.TryRepeat();
if (sessionRunning)
{
- logger.Info(">------ Session is running ------<");
+ RegisterSessionEvents();
+
+ logger.Info(">>>--- Session is running ---<<<");
runtimeWindow.HideProgressBar();
runtimeWindow.UpdateText(TextKey.RuntimeWindow_ApplicationRunning);
@@ -144,7 +158,7 @@ namespace SafeExamBrowser.Runtime.Behaviour
}
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...
uiFactory.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error);
@@ -160,28 +174,65 @@ namespace SafeExamBrowser.Runtime.Behaviour
runtimeWindow.Show();
runtimeWindow.BringToForeground();
runtimeWindow.ShowProgressBar();
- logger.Info(">------ Reverting session operations ------<");
+ logger.Info(">>>--- Reverting session operations ---<<<");
var success = sessionSequence.TryRevert();
if (success)
{
- logger.Info(">------ Session is terminated ------<");
+ logger.Info(">>>--- Session is terminated ---<<<");
sessionRunning = false;
-
- // TODO
}
else
{
- logger.Info(">------ Session reversion was erroneous! ------<");
-
- // TODO
+ logger.Info(">>>--- Session reversion was erroneous! ---<<<");
+ uiFactory.Show(TextKey.MessageBox_SessionStopError, TextKey.MessageBox_SessionStopErrorTitle, icon: MessageBoxIcon.Error);
}
}
+ 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()
{
- logger.Info("Received shutdown request from client application.");
+ logger.Info("Received shutdown request from the client application.");
shutdown.Invoke();
}
}
diff --git a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
index 328d4200..bce73790 100644
--- a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
+++ b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
@@ -18,6 +18,7 @@ namespace SafeExamBrowser.Runtime.Communication
{
internal class RuntimeHost : BaseHost, IRuntimeHost
{
+ private bool allowConnection = true;
private IConfigurationRepository configuration;
public Guid StartupToken { private get; set; }
@@ -33,7 +34,15 @@ namespace SafeExamBrowser.Runtime.Communication
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()
@@ -43,7 +52,6 @@ namespace SafeExamBrowser.Runtime.Communication
protected override Response OnReceive(Message message)
{
- // TODO
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
}