From 8a06a0fe98ca61b79b5d8d38ac643be5d1a4cf11 Mon Sep 17 00:00:00 2001 From: dbuechel Date: Tue, 27 Feb 2018 15:28:54 +0100 Subject: [PATCH] SEBWIN-219: Extended IPC mechanics with ping mechanism. --- .../Behaviour/ClientController.cs | 48 +++++++++--- .../Communication/ClientHost.cs | 15 +++- .../Communication/ICommunicationProxy.cs | 8 +- .../Communication/Messages/Message.cs | 2 +- .../Messages/SimpleMessagePurport.cs | 5 ++ .../Communication/Responses/Response.cs | 2 +- SafeExamBrowser.Contracts/I18n/TextKey.cs | 8 ++ .../Communication/BaseHost.cs | 20 +++-- .../Communication/BaseProxy.cs | 70 ++++++++++++++++- .../Communication/ClientProxy.cs | 12 ++- .../Communication/RuntimeProxy.cs | 5 -- SafeExamBrowser.Core/I18n/Text.xml | 24 ++++++ .../Operations/SessionSequenceOperation.cs | 18 +---- .../Behaviour/RuntimeController.cs | 77 +++++++++++++++---- .../Communication/RuntimeHost.cs | 12 ++- 15 files changed, 263 insertions(+), 63 deletions(-) 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); }