SEBWIN-219: Implemented basic shutdown mechanism.
This commit is contained in:
parent
ea97930033
commit
2424f2f1ed
28 changed files with 343 additions and 59 deletions
|
@ -26,6 +26,8 @@ namespace SafeExamBrowser.Client.Behaviour
|
|||
private ITaskbar taskbar;
|
||||
private IWindowMonitor windowMonitor;
|
||||
|
||||
public IClientHost ClientHost { private get; set; }
|
||||
|
||||
public ClientController(
|
||||
IDisplayMonitor displayMonitor,
|
||||
ILogger logger,
|
||||
|
@ -44,34 +46,46 @@ namespace SafeExamBrowser.Client.Behaviour
|
|||
this.windowMonitor = windowMonitor;
|
||||
}
|
||||
|
||||
public bool TryStart()
|
||||
{
|
||||
var success = operations.TryPerform();
|
||||
|
||||
// TODO
|
||||
|
||||
if (success)
|
||||
{
|
||||
RegisterEvents();
|
||||
runtime.InformClientReady();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
|
||||
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
|
||||
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
|
||||
DeregisterEvents();
|
||||
|
||||
// TODO
|
||||
|
||||
operations.TryRevert();
|
||||
}
|
||||
|
||||
public bool TryStart()
|
||||
private void RegisterEvents()
|
||||
{
|
||||
ClientHost.Shutdown += ClientHost_Shutdown;
|
||||
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
|
||||
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
|
||||
taskbar.QuitButtonClicked += Taskbar_QuitButtonClicked;
|
||||
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
var success = operations.TryPerform();
|
||||
|
||||
if (success)
|
||||
{
|
||||
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
|
||||
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
|
||||
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
|
||||
|
||||
runtime.InformClientReady();
|
||||
}
|
||||
|
||||
return success;
|
||||
private void DeregisterEvents()
|
||||
{
|
||||
ClientHost.Shutdown -= ClientHost_Shutdown;
|
||||
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
|
||||
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
|
||||
taskbar.QuitButtonClicked -= Taskbar_QuitButtonClicked;
|
||||
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
|
||||
}
|
||||
|
||||
private void DisplayMonitor_DisplaySettingsChanged()
|
||||
|
@ -94,6 +108,24 @@ namespace SafeExamBrowser.Client.Behaviour
|
|||
logger.Info("Desktop successfully restored.");
|
||||
}
|
||||
|
||||
private void ClientHost_Shutdown()
|
||||
{
|
||||
// TODO: Better use callback to Application.Shutdown() as in runtime?
|
||||
taskbar.Close();
|
||||
}
|
||||
|
||||
private void Taskbar_QuitButtonClicked()
|
||||
{
|
||||
// TODO: MessageBox asking whether user really wants to quit -> args.Cancel
|
||||
|
||||
var acknowledged = runtime.RequestShutdown();
|
||||
|
||||
if (!acknowledged)
|
||||
{
|
||||
logger.Warn("The runtime did not acknowledge the shutdown request!");
|
||||
}
|
||||
}
|
||||
|
||||
private void WindowMonitor_WindowChanged(IntPtr window)
|
||||
{
|
||||
var allowed = processMonitor.BelongsToAllowedProcess(window);
|
||||
|
|
|
@ -21,6 +21,8 @@ namespace SafeExamBrowser.Client.Communication
|
|||
|
||||
public Guid StartupToken { private get; set; }
|
||||
|
||||
public event CommunicationEventHandler Shutdown;
|
||||
|
||||
public ClientHost(string address, ILogger logger, int processId) : base(address, logger)
|
||||
{
|
||||
this.processId = processId;
|
||||
|
@ -49,6 +51,9 @@ namespace SafeExamBrowser.Client.Communication
|
|||
{
|
||||
case SimpleMessagePurport.Authenticate:
|
||||
return new AuthenticationResponse { ProcessId = processId };
|
||||
case SimpleMessagePurport.Shutdown:
|
||||
Shutdown?.Invoke();
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
}
|
||||
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
|
|
|
@ -102,6 +102,7 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
internal void LogShutdownInformation()
|
||||
{
|
||||
logger?.Log(string.Empty);
|
||||
logger?.Log($"# Client instance terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||
}
|
||||
|
||||
|
@ -153,6 +154,7 @@ namespace SafeExamBrowser.Client
|
|||
var operation = new CommunicationOperation(host, logger);
|
||||
|
||||
host.StartupToken = startupToken;
|
||||
ClientController.ClientHost = host;
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
|
|
@ -73,6 +73,12 @@ namespace SafeExamBrowser.Configuration
|
|||
};
|
||||
|
||||
settings.Browser.StartUrl = "https://www.duckduckgo.com";
|
||||
settings.Browser.AllowAddressBar = true;
|
||||
settings.Browser.AllowBackwardNavigation = true;
|
||||
settings.Browser.AllowDeveloperConsole = true;
|
||||
settings.Browser.AllowForwardNavigation = true;
|
||||
settings.Browser.AllowReloading = true;
|
||||
|
||||
settings.Taskbar.AllowApplicationLog = true;
|
||||
settings.Taskbar.AllowKeyboardLayout = true;
|
||||
settings.Taskbar.AllowWirelessNetwork = true;
|
||||
|
|
|
@ -6,11 +6,18 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.Behaviour
|
||||
{
|
||||
// TODO: Interface really needed?!
|
||||
public interface IClientController
|
||||
{
|
||||
/// <summary>
|
||||
/// The client host used for communication handling.
|
||||
/// </summary>
|
||||
IClientHost ClientHost { set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reverts any changes, releases all used resources and terminates the client.
|
||||
/// </summary>
|
||||
|
|
|
@ -16,5 +16,10 @@ namespace SafeExamBrowser.Contracts.Communication
|
|||
/// The startup token used for initial authentication.
|
||||
/// </summary>
|
||||
Guid StartupToken { set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the runtime commands the client to shutdown.
|
||||
/// </summary>
|
||||
event CommunicationEventHandler Shutdown;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@ namespace SafeExamBrowser.Contracts.Communication
|
|||
{
|
||||
public interface IClientProxy : ICommunicationProxy
|
||||
{
|
||||
/// <summary>
|
||||
/// Instructs the client to initiate its shutdown procedure.
|
||||
/// </summary>
|
||||
void InitiateShutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Instructs the client to submit its authentication data.
|
||||
/// </summary>
|
||||
|
|
|
@ -17,9 +17,19 @@ namespace SafeExamBrowser.Contracts.Communication
|
|||
/// </summary>
|
||||
Guid StartupToken { set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the client disconnected from the runtime.
|
||||
/// </summary>
|
||||
event CommunicationEventHandler ClientDisconnected;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired once the client has signaled that it is ready to operate.
|
||||
/// </summary>
|
||||
event CommunicationEventHandler ClientReady;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the client requests to shut down the application.
|
||||
/// </summary>
|
||||
event CommunicationEventHandler ShutdownRequested;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,18 @@ namespace SafeExamBrowser.Contracts.Communication
|
|||
/// <summary>
|
||||
/// Retrieves the application configuration from the runtime.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ServiceModel.CommunicationException">If the configuration could not be retrieved.</exception>
|
||||
ClientConfiguration GetConfiguration();
|
||||
|
||||
/// <summary>
|
||||
/// Informs the runtime that the client is ready.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ServiceModel.CommunicationException">If the runtime did not acknowledge the status update.</exception>
|
||||
void InformClientReady();
|
||||
|
||||
/// <summary>
|
||||
/// Requests the runtime to shut down the application. Returns <c>true</c> if the request was acknowledged, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool RequestShutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,5 +27,15 @@ namespace SafeExamBrowser.Contracts.Communication.Messages
|
|||
/// Sent from the client to the runtime to ask for the client configuration.
|
||||
/// </summary>
|
||||
ConfigurationNeeded,
|
||||
|
||||
/// <summary>
|
||||
/// Sent from the client to the runtime to request shutting down the application.
|
||||
/// </summary>
|
||||
RequestShutdown,
|
||||
|
||||
/// <summary>
|
||||
/// Sent form the runtime to the client to command the latter to shut itself down.
|
||||
/// </summary>
|
||||
Shutdown
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,15 @@
|
|||
|
||||
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
|
||||
{
|
||||
public delegate void QuitButtonClickedEventHandler();
|
||||
|
||||
public interface ITaskbar
|
||||
{
|
||||
/// <summary>
|
||||
/// Event fired when the user clicked the quit button in the taskbar.
|
||||
/// </summary>
|
||||
event QuitButtonClickedEventHandler QuitButtonClicked;
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given application button to the taskbar.
|
||||
/// </summary>
|
||||
|
@ -25,6 +32,11 @@ namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
|
|||
/// </summary>
|
||||
void AddSystemControl(ISystemControl control);
|
||||
|
||||
/// <summary>
|
||||
/// Closes the taskbar.
|
||||
/// </summary>
|
||||
void Close();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the absolute height of the taskbar (i.e. in physical pixels).
|
||||
/// </summary>
|
||||
|
|
|
@ -8,11 +8,28 @@
|
|||
|
||||
namespace SafeExamBrowser.Contracts.WindowsApi
|
||||
{
|
||||
public delegate void ProcessTerminatedEventHandler(int exitCode);
|
||||
|
||||
public interface IProcess
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether the process has been terminated.
|
||||
/// </summary>
|
||||
bool HasTerminated { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The process identifier.
|
||||
/// </summary>
|
||||
int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the process has terminated.
|
||||
/// </summary>
|
||||
event ProcessTerminatedEventHandler Terminated;
|
||||
|
||||
/// <summary>
|
||||
/// Immediately terminates the process.
|
||||
/// </summary>
|
||||
void Kill();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,9 +97,7 @@ namespace SafeExamBrowser.Core.Communication
|
|||
{
|
||||
lock (@lock)
|
||||
{
|
||||
Response response = null;
|
||||
|
||||
logger.Debug($"Received message '{ToString(message)}'.");
|
||||
var response = default(Response);
|
||||
|
||||
if (IsAuthorized(message?.CommunicationToken))
|
||||
{
|
||||
|
@ -113,7 +111,7 @@ namespace SafeExamBrowser.Core.Communication
|
|||
}
|
||||
}
|
||||
|
||||
logger.Debug($"Sending response '{ToString(response)}'.");
|
||||
logger.Debug($"Received message '{ToString(message)}', sending response '{ToString(response)}'.");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,16 @@ namespace SafeExamBrowser.Core.Communication
|
|||
return Send(new SimpleMessage(purport));
|
||||
}
|
||||
|
||||
protected string ToString(Message message)
|
||||
{
|
||||
return message != null ? message.ToString() : "<null>";
|
||||
}
|
||||
|
||||
protected string ToString(Response response)
|
||||
{
|
||||
return response != null ? response.ToString() : "<null>";
|
||||
}
|
||||
|
||||
private void BaseProxy_Closed(object sender, EventArgs e)
|
||||
{
|
||||
Logger.Debug("Communication channel has been closed.");
|
||||
|
@ -122,15 +132,5 @@ namespace SafeExamBrowser.Core.Communication
|
|||
{
|
||||
return channel == null ? "null" : $"in state '{(channel as ICommunicationObject).State}'";
|
||||
}
|
||||
|
||||
private string ToString(Message message)
|
||||
{
|
||||
return message != null ? message.ToString() : "<null>";
|
||||
}
|
||||
|
||||
private string ToString(Response response)
|
||||
{
|
||||
return response != null ? response.ToString() : "<null>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,16 @@ namespace SafeExamBrowser.Core.Communication
|
|||
{
|
||||
}
|
||||
|
||||
public void InitiateShutdown()
|
||||
{
|
||||
var response = Send(SimpleMessagePurport.Shutdown);
|
||||
|
||||
if (response is SimpleResponse simpleMessage && simpleMessage.Purport == SimpleResponsePurport.Acknowledged)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationResponse RequestAuthentication()
|
||||
{
|
||||
var response = Send(SimpleMessagePurport.Authenticate);
|
||||
|
|
|
@ -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;
|
||||
|
@ -22,12 +23,37 @@ namespace SafeExamBrowser.Core.Communication
|
|||
|
||||
public ClientConfiguration GetConfiguration()
|
||||
{
|
||||
return ((ConfigurationResponse) Send(SimpleMessagePurport.ConfigurationNeeded)).Configuration;
|
||||
var response = Send(SimpleMessagePurport.ConfigurationNeeded);
|
||||
|
||||
if (response is ConfigurationResponse configurationResponse)
|
||||
{
|
||||
return configurationResponse.Configuration;
|
||||
}
|
||||
|
||||
throw new CommunicationException($"Could not retrieve client configuration! Received: {ToString(response)}.");
|
||||
}
|
||||
|
||||
public void InformClientReady()
|
||||
{
|
||||
Send(SimpleMessagePurport.ClientIsReady);
|
||||
var response = Send(SimpleMessagePurport.ClientIsReady);
|
||||
|
||||
if (!IsAcknowledgeResponse(response))
|
||||
{
|
||||
throw new CommunicationException($"Runtime did not acknowledge that client is ready! Response: {ToString(response)}.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool RequestShutdown()
|
||||
{
|
||||
var response = Send(SimpleMessagePurport.RequestShutdown);
|
||||
var acknowledged = IsAcknowledgeResponse(response);
|
||||
|
||||
return acknowledged;
|
||||
}
|
||||
|
||||
private bool IsAcknowledgeResponse(Response response)
|
||||
{
|
||||
return response is SimpleResponse simpleResponse && simpleResponse.Purport == SimpleResponsePurport.Acknowledged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace SafeExamBrowser.Core.Communication
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// TODO: Send(new StopSessionMessage { SessionId = sessionId });
|
||||
}
|
||||
|
||||
private bool IgnoreOperation(string operationName)
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
{
|
||||
internal abstract class SessionSequenceOperation : IOperation
|
||||
{
|
||||
private const int TEN_SECONDS = 10000;
|
||||
|
||||
private bool sessionRunning;
|
||||
private IClientProxy client;
|
||||
private IConfigurationRepository configuration;
|
||||
|
@ -68,7 +70,6 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
service.StopSession(session.Id);
|
||||
logger.Error("Failed to start client!", e);
|
||||
}
|
||||
|
||||
|
@ -80,6 +81,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
{
|
||||
Abort = true;
|
||||
logger.Info($"Failed to start new session! Aborting...");
|
||||
service.StopSession(session.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,8 +94,14 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
|
||||
service.StopSession(session.Id);
|
||||
|
||||
// TODO:
|
||||
// - Terminate client (or does it terminate itself?)
|
||||
try
|
||||
{
|
||||
StopClient();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to terminate client!", e);
|
||||
}
|
||||
|
||||
sessionRunning = false;
|
||||
logger.Info($"Successfully stopped session with identifier '{session.Id}'.");
|
||||
|
@ -102,9 +110,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
|
||||
private void StartClient()
|
||||
{
|
||||
const int TEN_SECONDS = 10000;
|
||||
|
||||
var clientStarted = false;
|
||||
var clientReady = false;
|
||||
var clientReadyEvent = new AutoResetEvent(false);
|
||||
var clientReadyEventHandler = new CommunicationEventHandler(() => clientReadyEvent.Set());
|
||||
var clientExecutable = configuration.RuntimeInfo.ClientExecutablePath;
|
||||
|
@ -113,13 +119,14 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
var token = session.StartupToken.ToString("D");
|
||||
|
||||
runtimeHost.ClientReady += clientReadyEventHandler;
|
||||
session.ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token);
|
||||
|
||||
clientStarted = clientReadyEvent.WaitOne(TEN_SECONDS);
|
||||
session.ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token);
|
||||
clientReady = clientReadyEvent.WaitOne(TEN_SECONDS);
|
||||
|
||||
runtimeHost.ClientReady -= clientReadyEventHandler;
|
||||
|
||||
// TODO: Check if client process alive!
|
||||
if (clientStarted)
|
||||
if (clientReady)
|
||||
{
|
||||
if (client.Connect(session.StartupToken))
|
||||
{
|
||||
|
@ -145,5 +152,71 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
|||
logger.Error($"Failed to start client within {TEN_SECONDS / 1000} seconds!");
|
||||
}
|
||||
}
|
||||
|
||||
private void StopClient()
|
||||
{
|
||||
var disconnected = false;
|
||||
var disconnectedEvent = new AutoResetEvent(false);
|
||||
var disconnectedEventHandler = new CommunicationEventHandler(() => disconnectedEvent.Set());
|
||||
|
||||
var terminated = false;
|
||||
var terminatedEvent = new AutoResetEvent(false);
|
||||
var terminatedEventHandler = new ProcessTerminatedEventHandler((_) => terminatedEvent.Set());
|
||||
|
||||
runtimeHost.ClientDisconnected += disconnectedEventHandler;
|
||||
session.ClientProcess.Terminated += terminatedEventHandler;
|
||||
|
||||
client.InitiateShutdown();
|
||||
client.Disconnect();
|
||||
|
||||
disconnected = disconnectedEvent.WaitOne(TEN_SECONDS);
|
||||
terminated = terminatedEvent.WaitOne(TEN_SECONDS);
|
||||
|
||||
runtimeHost.ClientDisconnected -= disconnectedEventHandler;
|
||||
session.ClientProcess.Terminated -= terminatedEventHandler;
|
||||
|
||||
if (disconnected && terminated)
|
||||
{
|
||||
logger.Info("Client has been successfully terminated.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!disconnected)
|
||||
{
|
||||
logger.Error($"Client failed to disconnect within {TEN_SECONDS / 1000} seconds!");
|
||||
}
|
||||
else if (!terminated)
|
||||
{
|
||||
logger.Error($"Client failed to terminate within {TEN_SECONDS / 1000} seconds!");
|
||||
}
|
||||
|
||||
KillClient();
|
||||
}
|
||||
}
|
||||
|
||||
private void KillClient(int attempt = 0)
|
||||
{
|
||||
const int MAX_ATTEMPTS = 5;
|
||||
|
||||
if (attempt == MAX_ATTEMPTS)
|
||||
{
|
||||
logger.Error($"Failed to kill client process within {MAX_ATTEMPTS} attempts!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
logger.Info($"Killing client process with ID = {session.ClientProcess.Id}.");
|
||||
session.ClientProcess.Kill();
|
||||
|
||||
if (session.ClientProcess.HasTerminated)
|
||||
{
|
||||
logger.Info("Client process has terminated.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to kill client process. Trying again...");
|
||||
KillClient(attempt++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ namespace SafeExamBrowser.Runtime.Behaviour
|
|||
logger.Subscribe(runtimeWindow);
|
||||
|
||||
splashScreen.Hide();
|
||||
runtimeHost.ShutdownRequested += RuntimeHost_ShutdownRequested;
|
||||
|
||||
StartSession(true);
|
||||
}
|
||||
|
@ -169,5 +170,11 @@ namespace SafeExamBrowser.Runtime.Behaviour
|
|||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
private void RuntimeHost_ShutdownRequested()
|
||||
{
|
||||
logger.Info("Received shutdown request from client application.");
|
||||
shutdown.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Communication.Messages;
|
||||
using SafeExamBrowser.Contracts.Communication.Responses;
|
||||
|
@ -22,7 +23,9 @@ namespace SafeExamBrowser.Runtime.Communication
|
|||
|
||||
public Guid StartupToken { private get; set; }
|
||||
|
||||
public event CommunicationEventHandler ClientDisconnected;
|
||||
public event CommunicationEventHandler ClientReady;
|
||||
public event CommunicationEventHandler ShutdownRequested;
|
||||
|
||||
public RuntimeHost(string address, IConfigurationRepository configuration, ILogger logger) : base(address, logger)
|
||||
{
|
||||
|
@ -36,7 +39,7 @@ namespace SafeExamBrowser.Runtime.Communication
|
|||
|
||||
protected override void OnDisconnect()
|
||||
{
|
||||
// TODO
|
||||
Task.Run(() => ClientDisconnected?.Invoke());
|
||||
}
|
||||
|
||||
protected override Response OnReceive(Message message)
|
||||
|
@ -54,6 +57,9 @@ namespace SafeExamBrowser.Runtime.Communication
|
|||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
case SimpleMessagePurport.ConfigurationNeeded:
|
||||
return new ConfigurationResponse { Configuration = configuration.BuildClientConfiguration() };
|
||||
case SimpleMessagePurport.RequestShutdown:
|
||||
ShutdownRequested?.Invoke();
|
||||
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
|
||||
}
|
||||
|
||||
return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace SafeExamBrowser.Runtime
|
|||
var args = Environment.GetCommandLineArgs();
|
||||
var configuration = new ConfigurationRepository();
|
||||
var nativeMethods = new NativeMethods();
|
||||
Action shutdown = Application.Current.Shutdown;
|
||||
void shutdown() => Application.Current.Dispatcher.BeginInvoke(new Action(Application.Current.Shutdown));
|
||||
|
||||
logger = new Logger();
|
||||
runtimeInfo = configuration.RuntimeInfo;
|
||||
|
@ -75,12 +75,10 @@ namespace SafeExamBrowser.Runtime
|
|||
|
||||
internal void LogStartupInformation()
|
||||
{
|
||||
var titleLine = $"/* {runtimeInfo.ProgramTitle}, Version {runtimeInfo.ProgramVersion}{Environment.NewLine}";
|
||||
var copyrightLine = $"/* {runtimeInfo.ProgramCopyright}{Environment.NewLine}";
|
||||
var emptyLine = $"/* {Environment.NewLine}";
|
||||
var githubLine = $"/* Please visit https://www.github.com/SafeExamBrowser for more information.";
|
||||
|
||||
logger.Log($"{titleLine}{copyrightLine}{emptyLine}{githubLine}");
|
||||
logger.Log($"/* {runtimeInfo.ProgramTitle}, Version {runtimeInfo.ProgramVersion}");
|
||||
logger.Log($"/* {runtimeInfo.ProgramCopyright}");
|
||||
logger.Log($"/* ");
|
||||
logger.Log($"/* Please visit https://www.github.com/SafeExamBrowser for more information.");
|
||||
logger.Log(string.Empty);
|
||||
logger.Log($"# Application started at {runtimeInfo.ApplicationStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||
logger.Log($"# Running on {systemInfo.OperatingSystemInfo}");
|
||||
|
|
|
@ -9,12 +9,15 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
|
||||
using SafeExamBrowser.UserInterface.Classic.Utilities;
|
||||
|
||||
namespace SafeExamBrowser.UserInterface.Classic.Controls
|
||||
{
|
||||
public partial class QuitButton : UserControl
|
||||
{
|
||||
public QuitButtonClickedEventHandler Clicked;
|
||||
|
||||
public QuitButton()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
@ -23,7 +26,7 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls
|
|||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Application.Current.MainWindow.Close();
|
||||
Clicked?.Invoke();
|
||||
}
|
||||
|
||||
private void LoadIcon()
|
||||
|
|
|
@ -25,6 +25,6 @@
|
|||
<local:DateTimeControl Grid.Column="1" Foreground="DimGray" />
|
||||
<StackPanel Grid.Column="2" x:Name="NotificationStackPanel" Margin="5,0,0,0" Orientation="Horizontal" VerticalAlignment="Stretch" />
|
||||
<StackPanel Grid.Column="3" x:Name="SystemControlStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
|
||||
<local:QuitButton Grid.Column="4" />
|
||||
<local:QuitButton Grid.Column="4" x:Name="QuitButton" />
|
||||
</Grid>
|
||||
</Window>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
|
||||
|
@ -17,14 +18,17 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
{
|
||||
private ILogger logger;
|
||||
|
||||
public event QuitButtonClickedEventHandler QuitButtonClicked;
|
||||
|
||||
public Taskbar(ILogger logger)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.logger = logger;
|
||||
|
||||
Loaded += (o, args) => InitializeBounds();
|
||||
Closing += Taskbar_Closing;
|
||||
Loaded += (o, args) => InitializeBounds();
|
||||
QuitButton.Clicked += () => QuitButtonClicked?.Invoke();
|
||||
}
|
||||
|
||||
public void AddApplication(IApplicationButton button)
|
||||
|
@ -51,6 +55,11 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
}
|
||||
}
|
||||
|
||||
public new void Close()
|
||||
{
|
||||
Dispatcher.Invoke(base.Close);
|
||||
}
|
||||
|
||||
public int GetAbsoluteHeight()
|
||||
{
|
||||
return Dispatcher.Invoke(() =>
|
||||
|
@ -78,7 +87,7 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
});
|
||||
}
|
||||
|
||||
private void Taskbar_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
private void Taskbar_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
foreach (var child in SystemControlStackPanel.Children)
|
||||
{
|
||||
|
|
|
@ -8,11 +8,14 @@
|
|||
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
|
||||
|
||||
namespace SafeExamBrowser.UserInterface.Windows10.Controls
|
||||
{
|
||||
public partial class QuitButton : UserControl
|
||||
{
|
||||
public event QuitButtonClickedEventHandler Clicked;
|
||||
|
||||
public QuitButton()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
@ -20,7 +23,7 @@ namespace SafeExamBrowser.UserInterface.Windows10.Controls
|
|||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Application.Current.MainWindow.Close();
|
||||
Clicked?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<StackPanel Grid.Column="1" x:Name="NotificationStackPanel" Margin="5,0,0,0" Orientation="Horizontal" VerticalAlignment="Stretch" />
|
||||
<StackPanel Grid.Column="2" x:Name="SystemControlStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
|
||||
<local:DateTimeControl Grid.Column="3" />
|
||||
<local:QuitButton Grid.Column="4" />
|
||||
<local:QuitButton Grid.Column="4" x:Name="QuitButton" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
|
||||
|
@ -17,6 +18,8 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
{
|
||||
private ILogger logger;
|
||||
|
||||
public event QuitButtonClickedEventHandler QuitButtonClicked;
|
||||
|
||||
public Taskbar(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
|
@ -25,6 +28,7 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
|
||||
Loaded += (o, args) => InitializeBounds();
|
||||
Closing += Taskbar_Closing;
|
||||
QuitButtonClicked += Taskbar_QuitButtonClicked;
|
||||
}
|
||||
|
||||
public void AddApplication(IApplicationButton button)
|
||||
|
@ -51,6 +55,11 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
}
|
||||
}
|
||||
|
||||
public new void Close()
|
||||
{
|
||||
Dispatcher.Invoke(base.Close);
|
||||
}
|
||||
|
||||
public int GetAbsoluteHeight()
|
||||
{
|
||||
return Dispatcher.Invoke(() =>
|
||||
|
@ -78,7 +87,12 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
});
|
||||
}
|
||||
|
||||
private void Taskbar_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
private void Taskbar_QuitButtonClicked()
|
||||
{
|
||||
QuitButtonClicked?.Invoke();
|
||||
}
|
||||
|
||||
private void Taskbar_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
foreach (var child in SystemControlStackPanel.Children)
|
||||
{
|
||||
|
|
|
@ -14,14 +14,33 @@ namespace SafeExamBrowser.WindowsApi
|
|||
{
|
||||
private System.Diagnostics.Process process;
|
||||
|
||||
public event ProcessTerminatedEventHandler Terminated;
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return process.Id; }
|
||||
}
|
||||
|
||||
public bool HasTerminated
|
||||
{
|
||||
get { return process.HasExited; }
|
||||
}
|
||||
|
||||
public Process(int id)
|
||||
{
|
||||
process = System.Diagnostics.Process.GetProcessById(id);
|
||||
process.Exited += Process_Exited;
|
||||
process.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
public void Kill()
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
|
||||
private void Process_Exited(object sender, System.EventArgs e)
|
||||
{
|
||||
Terminated?.Invoke(process.ExitCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue