SEBWIN-477: Minor refactoring.
This commit is contained in:
parent
5709f6a3dc
commit
3a39784af2
11 changed files with 78 additions and 76 deletions
SafeExamBrowser.Client
SafeExamBrowser.Server.Contracts
SafeExamBrowser.Server
SafeExamBrowser.UserInterface.Contracts/Windows
SafeExamBrowser.UserInterface.Desktop/Windows
SafeExamBrowser.UserInterface.Mobile/Windows
|
@ -217,24 +217,11 @@ namespace SafeExamBrowser.Client
|
||||||
if (Server != null)
|
if (Server != null)
|
||||||
{
|
{
|
||||||
Server.LockScreenConfirmed += Server_LockScreenConfirmed;
|
Server.LockScreenConfirmed += Server_LockScreenConfirmed;
|
||||||
Server.TerminationRequested += Server_TerminationRequested;
|
|
||||||
Server.LockScreenRequested += Server_LockScreenRequested;
|
Server.LockScreenRequested += Server_LockScreenRequested;
|
||||||
|
Server.TerminationRequested += Server_TerminationRequested;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Server_LockScreenRequested(string message)
|
|
||||||
{
|
|
||||||
logger.Info("Received lock screen event from SEB Server.");
|
|
||||||
var title = text.Get(TextKey.LockScreen_Title);
|
|
||||||
ShowLockScreen(message, title, Enumerable.Empty<LockScreenOption>());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Server_LockScreenConfirmed()
|
|
||||||
{
|
|
||||||
logger.Info("Closing lock screen as requested by the server...");
|
|
||||||
lockScreen?.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Taskbar_LoseFocusRequested(bool forward)
|
private void Taskbar_LoseFocusRequested(bool forward)
|
||||||
{
|
{
|
||||||
Browser.Focus(forward);
|
Browser.Focus(forward);
|
||||||
|
@ -277,8 +264,8 @@ namespace SafeExamBrowser.Client
|
||||||
if (Server != null)
|
if (Server != null)
|
||||||
{
|
{
|
||||||
Server.LockScreenConfirmed -= Server_LockScreenConfirmed;
|
Server.LockScreenConfirmed -= Server_LockScreenConfirmed;
|
||||||
Server.TerminationRequested -= Server_TerminationRequested;
|
|
||||||
Server.LockScreenRequested -= Server_LockScreenRequested;
|
Server.LockScreenRequested -= Server_LockScreenRequested;
|
||||||
|
Server.TerminationRequested -= Server_TerminationRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var activator in context.Activators.OfType<ITerminationActivator>())
|
foreach (var activator in context.Activators.OfType<ITerminationActivator>())
|
||||||
|
@ -659,6 +646,19 @@ namespace SafeExamBrowser.Client
|
||||||
shutdown.Invoke();
|
shutdown.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Server_LockScreenConfirmed()
|
||||||
|
{
|
||||||
|
logger.Info("Closing lock screen as requested by the server...");
|
||||||
|
lockScreen?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Server_LockScreenRequested(string message)
|
||||||
|
{
|
||||||
|
logger.Info("Received lock screen event from SEB Server.");
|
||||||
|
var title = text.Get(TextKey.LockScreen_Title);
|
||||||
|
ShowLockScreen(message, title, Enumerable.Empty<LockScreenOption>());
|
||||||
|
}
|
||||||
|
|
||||||
private void Server_TerminationRequested()
|
private void Server_TerminationRequested()
|
||||||
{
|
{
|
||||||
logger.Info("Attempting to shutdown as requested by the server...");
|
logger.Info("Attempting to shutdown as requested by the server...");
|
||||||
|
@ -787,11 +787,11 @@ namespace SafeExamBrowser.Client
|
||||||
private LockScreenResult ShowLockScreen(string message, string title, IEnumerable<LockScreenOption> options)
|
private LockScreenResult ShowLockScreen(string message, string title, IEnumerable<LockScreenOption> options)
|
||||||
{
|
{
|
||||||
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
|
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
|
||||||
lockScreen = uiFactory.CreateLockScreen(message, title, options);
|
|
||||||
var result = default(LockScreenResult);
|
var result = default(LockScreenResult);
|
||||||
|
|
||||||
logger.Info("Showing lock screen...");
|
logger.Info("Showing lock screen...");
|
||||||
PauseActivators();
|
PauseActivators();
|
||||||
|
lockScreen = uiFactory.CreateLockScreen(message, title, options);
|
||||||
lockScreen.Show();
|
lockScreen.Show();
|
||||||
|
|
||||||
if (Settings.SessionMode == SessionMode.Server)
|
if (Settings.SessionMode == SessionMode.Server)
|
||||||
|
@ -808,11 +808,11 @@ namespace SafeExamBrowser.Client
|
||||||
{
|
{
|
||||||
result = lockScreen.WaitForResult();
|
result = lockScreen.WaitForResult();
|
||||||
|
|
||||||
if (result.Canceled)
|
if (result.Canceled)
|
||||||
{
|
{
|
||||||
logger.Info("The lock screen has been automaticaly canceled.");
|
logger.Info("The lock screen has been automaticaly canceled.");
|
||||||
unlocked = true;
|
unlocked = true;
|
||||||
}
|
}
|
||||||
else if (hasQuitPassword)
|
else if (hasQuitPassword)
|
||||||
{
|
{
|
||||||
var passwordHash = hashAlgorithm.GenerateHashFor(result.Password);
|
var passwordHash = hashAlgorithm.GenerateHashFor(result.Password);
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
using System;
|
/*
|
||||||
using System.Collections.Generic;
|
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
|
||||||
using System.Linq;
|
*
|
||||||
using System.Text;
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
using System.Threading.Tasks;
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
namespace SafeExamBrowser.Server.Contracts.Events
|
namespace SafeExamBrowser.Server.Contracts.Events
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event handler used to handle a requested lock screen from SEB Server
|
/// Event handler used to indicate that a lock screen instruction has been received.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public delegate void LockScreenRequestedEventHandler(String message);
|
public delegate void LockScreenRequestedEventHandler(string message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace SafeExamBrowser.Server.Contracts
|
||||||
event ServerEventHandler LockScreenConfirmed;
|
event ServerEventHandler LockScreenConfirmed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event fired when proxy receives a lock screen instruction.
|
/// Event fired when the proxy receives a lock screen instruction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event LockScreenRequestedEventHandler LockScreenRequested;
|
event LockScreenRequestedEventHandler LockScreenRequested;
|
||||||
|
|
||||||
|
@ -47,13 +47,18 @@ namespace SafeExamBrowser.Server.Contracts
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event fired when the proxy detects an instruction to terminate SEB.
|
/// Event fired when the proxy detects an instruction to terminate SEB.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event TerminationRequestedEventHandler TerminationRequested;
|
event TerminationRequestedEventHandler TerminationRequested;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to initialize a connection with the server.
|
/// Attempts to initialize a connection with the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ServerResponse Connect();
|
ServerResponse Connect();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a lock screen confirm notification to the server.
|
||||||
|
/// </summary>
|
||||||
|
ServerResponse ConfirmLockScreen();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Terminates a connection with the server.
|
/// Terminates a connection with the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -62,7 +67,7 @@ namespace SafeExamBrowser.Server.Contracts
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves a list of all currently available exams, or a list containing the specified exam.
|
/// Retrieves a list of all currently available exams, or a list containing the specified exam.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ServerResponse<IEnumerable<Exam>> GetAvailableExams(string examId = default(string));
|
ServerResponse<IEnumerable<Exam>> GetAvailableExams(string examId = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the URI of the configuration file for the given exam.
|
/// Retrieves the URI of the configuration file for the given exam.
|
||||||
|
@ -84,16 +89,16 @@ namespace SafeExamBrowser.Server.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Initialize(string api, string connectionToken, string examId, string oauth2Token, ServerSettings settings);
|
void Initialize(string api, string connectionToken, string examId, string oauth2Token, ServerSettings settings);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a lock screen notification to the server.
|
||||||
|
/// </summary>
|
||||||
|
ServerResponse LockScreen(string message = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends a lower hand notification to the server.
|
/// Sends a lower hand notification to the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ServerResponse LowerHand();
|
ServerResponse LowerHand();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a lock screen confirm notification to the server.
|
|
||||||
/// </summary>
|
|
||||||
ServerResponse ConfirmLockScreen();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends the given user session identifier of a LMS and thus establishes a connection with the server.
|
/// Sends the given user session identifier of a LMS and thus establishes a connection with the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -112,11 +117,6 @@ namespace SafeExamBrowser.Server.Contracts
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends a raise hand notification to the server.
|
/// Sends a raise hand notification to the server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ServerResponse RaiseHand(string message = default(string));
|
ServerResponse RaiseHand(string message = default);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a lock screen notification to the server.
|
|
||||||
/// </summary>
|
|
||||||
ServerResponse LockScreen(string message = default(string));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,11 @@ namespace SafeExamBrowser.Server.Data
|
||||||
internal bool AllowChat { get; set; }
|
internal bool AllowChat { get; set; }
|
||||||
internal int Id { get; set; }
|
internal int Id { get; set; }
|
||||||
internal ProctoringInstructionEventArgs Instruction { get; set; }
|
internal ProctoringInstructionEventArgs Instruction { get; set; }
|
||||||
|
internal string Message { get; set; }
|
||||||
internal bool ReceiveAudio { get; set; }
|
internal bool ReceiveAudio { get; set; }
|
||||||
internal bool ReceiveVideo { get; set; }
|
internal bool ReceiveVideo { get; set; }
|
||||||
internal string Type { get; set; }
|
internal string Type { get; set; }
|
||||||
|
|
||||||
internal string Message { get; set; }
|
|
||||||
|
|
||||||
internal Attributes()
|
internal Attributes()
|
||||||
{
|
{
|
||||||
Instruction = new ProctoringInstructionEventArgs();
|
Instruction = new ProctoringInstructionEventArgs();
|
||||||
|
|
|
@ -10,10 +10,10 @@ namespace SafeExamBrowser.Server.Data
|
||||||
{
|
{
|
||||||
internal sealed class Instructions
|
internal sealed class Instructions
|
||||||
{
|
{
|
||||||
|
internal const string LOCK_SCREEN = "SEB_FORCE_LOCK_SCREEN";
|
||||||
internal const string NOTIFICATION_CONFIRM = "NOTIFICATION_CONFIRM";
|
internal const string NOTIFICATION_CONFIRM = "NOTIFICATION_CONFIRM";
|
||||||
internal const string PROCTORING = "SEB_PROCTORING";
|
internal const string PROCTORING = "SEB_PROCTORING";
|
||||||
internal const string PROCTORING_RECONFIGURATION = "SEB_RECONFIGURE_SETTINGS";
|
internal const string PROCTORING_RECONFIGURATION = "SEB_RECONFIGURE_SETTINGS";
|
||||||
internal const string QUIT = "SEB_QUIT";
|
internal const string QUIT = "SEB_QUIT";
|
||||||
internal const string LOCK_SCREEN = "SEB_FORCE_LOCK_SCREEN";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,6 +216,9 @@ namespace SafeExamBrowser.Server
|
||||||
|
|
||||||
switch (instruction)
|
switch (instruction)
|
||||||
{
|
{
|
||||||
|
case Instructions.LOCK_SCREEN:
|
||||||
|
ParseLockScreenInstruction(attributes, attributesJson);
|
||||||
|
break;
|
||||||
case Instructions.NOTIFICATION_CONFIRM:
|
case Instructions.NOTIFICATION_CONFIRM:
|
||||||
ParseNotificationConfirmation(attributes, attributesJson);
|
ParseNotificationConfirmation(attributes, attributesJson);
|
||||||
break;
|
break;
|
||||||
|
@ -225,9 +228,6 @@ namespace SafeExamBrowser.Server
|
||||||
case Instructions.PROCTORING_RECONFIGURATION:
|
case Instructions.PROCTORING_RECONFIGURATION:
|
||||||
ParseReconfigurationInstruction(attributes, attributesJson);
|
ParseReconfigurationInstruction(attributes, attributesJson);
|
||||||
break;
|
break;
|
||||||
case Instructions.LOCK_SCREEN:
|
|
||||||
ParseLockScreenInstruction(attributes, attributesJson);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
|
|
|
@ -50,15 +50,14 @@ namespace SafeExamBrowser.Server
|
||||||
|
|
||||||
private ApiVersion1 api;
|
private ApiVersion1 api;
|
||||||
private string connectionToken;
|
private string connectionToken;
|
||||||
private int currentPowerSupplyValue;
|
|
||||||
private bool connectedToPowergrid;
|
private bool connectedToPowergrid;
|
||||||
|
private int currentLockScreenId;
|
||||||
|
private int currentPowerSupplyValue;
|
||||||
|
private int currentRaisHandId;
|
||||||
private int currentWlanValue;
|
private int currentWlanValue;
|
||||||
private string examId;
|
private string examId;
|
||||||
private int notificationId;
|
|
||||||
private int currentRaisHandId;
|
|
||||||
private int currentLockScreenId;
|
|
||||||
|
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
|
private int notificationId;
|
||||||
private string oauth2Token;
|
private string oauth2Token;
|
||||||
private int pingNumber;
|
private int pingNumber;
|
||||||
private ServerSettings settings;
|
private ServerSettings settings;
|
||||||
|
@ -472,12 +471,15 @@ namespace SafeExamBrowser.Server
|
||||||
{
|
{
|
||||||
switch (instruction)
|
switch (instruction)
|
||||||
{
|
{
|
||||||
case Instructions.NOTIFICATION_CONFIRM when attributes.Type == "raisehand":
|
case Instructions.LOCK_SCREEN:
|
||||||
Task.Run(() => HandConfirmed?.Invoke());
|
Task.Run(() => LockScreenRequested?.Invoke(attributes.Message));
|
||||||
break;
|
break;
|
||||||
case Instructions.NOTIFICATION_CONFIRM when attributes.Type == "lockscreen":
|
case Instructions.NOTIFICATION_CONFIRM when attributes.Type == "lockscreen":
|
||||||
Task.Run(() => LockScreenConfirmed?.Invoke());
|
Task.Run(() => LockScreenConfirmed?.Invoke());
|
||||||
break;
|
break;
|
||||||
|
case Instructions.NOTIFICATION_CONFIRM when attributes.Type == "raisehand":
|
||||||
|
Task.Run(() => HandConfirmed?.Invoke());
|
||||||
|
break;
|
||||||
case Instructions.PROCTORING:
|
case Instructions.PROCTORING:
|
||||||
Task.Run(() => ProctoringInstructionReceived?.Invoke(attributes.Instruction));
|
Task.Run(() => ProctoringInstructionReceived?.Invoke(attributes.Instruction));
|
||||||
break;
|
break;
|
||||||
|
@ -487,9 +489,6 @@ namespace SafeExamBrowser.Server
|
||||||
case Instructions.QUIT:
|
case Instructions.QUIT:
|
||||||
Task.Run(() => TerminationRequested?.Invoke());
|
Task.Run(() => TerminationRequested?.Invoke());
|
||||||
break;
|
break;
|
||||||
case Instructions.LOCK_SCREEN:
|
|
||||||
Task.Run(() => LockScreenRequested?.Invoke(attributes.Message));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instructionConfirmation != default)
|
if (instructionConfirmation != default)
|
||||||
|
|
|
@ -15,9 +15,8 @@ namespace SafeExamBrowser.UserInterface.Contracts.Windows.Data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LockScreenResult
|
public class LockScreenResult
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is been set if the lock screen was canceled from another process (E.g.: from SEB Server instruction)
|
/// Indicates that the lock screen has been canceled (e.g. via a server instruction).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Canceled { get; set; }
|
public bool Canceled { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,14 @@ namespace SafeExamBrowser.UserInterface.Contracts.Windows
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ILockScreen : IWindow
|
public interface ILockScreen : IWindow
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cancels the <see cref="WaitForResult"/> operation and closes the lock screen.
|
||||||
|
/// </summary>
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Waits for the user to provide the required input to unlock the application.
|
/// Waits for the user to provide the required input to unlock the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
LockScreenResult WaitForResult();
|
LockScreenResult WaitForResult();
|
||||||
/// <summary>
|
|
||||||
/// This cancel a waiting thread for LockScreenResult and force the lock screen to close
|
|
||||||
/// </summary>
|
|
||||||
void Cancel();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
|
||||||
{
|
{
|
||||||
private readonly AutoResetEvent autoResetEvent;
|
private readonly AutoResetEvent autoResetEvent;
|
||||||
private readonly IText text;
|
private readonly IText text;
|
||||||
private bool canceled = false;
|
|
||||||
|
|
||||||
|
private bool canceled;
|
||||||
private IList<Window> windows;
|
private IList<Window> windows;
|
||||||
|
|
||||||
event WindowClosedEventHandler IWindow.Closed
|
event WindowClosedEventHandler IWindow.Closed
|
||||||
|
@ -57,6 +57,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
|
||||||
Dispatcher.Invoke(Activate);
|
Dispatcher.Invoke(Activate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
canceled = true;
|
||||||
|
autoResetEvent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
public new void Close()
|
public new void Close()
|
||||||
{
|
{
|
||||||
Dispatcher.Invoke(CloseAll);
|
Dispatcher.Invoke(CloseAll);
|
||||||
|
@ -87,6 +93,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
|
||||||
});
|
});
|
||||||
|
|
||||||
result.Canceled = canceled;
|
result.Canceled = canceled;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,11 +181,5 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
|
||||||
autoResetEvent.Set();
|
autoResetEvent.Set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cancel()
|
|
||||||
{
|
|
||||||
canceled = true;
|
|
||||||
autoResetEvent.Set();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
|
||||||
{
|
{
|
||||||
private readonly AutoResetEvent autoResetEvent;
|
private readonly AutoResetEvent autoResetEvent;
|
||||||
private readonly IText text;
|
private readonly IText text;
|
||||||
private bool canceled = false;
|
|
||||||
|
|
||||||
|
private bool canceled;
|
||||||
private IList<Window> windows;
|
private IList<Window> windows;
|
||||||
|
|
||||||
event WindowClosedEventHandler IWindow.Closed
|
event WindowClosedEventHandler IWindow.Closed
|
||||||
|
@ -57,6 +57,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
|
||||||
Dispatcher.Invoke(Activate);
|
Dispatcher.Invoke(Activate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
canceled = true;
|
||||||
|
autoResetEvent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
public new void Close()
|
public new void Close()
|
||||||
{
|
{
|
||||||
Dispatcher.Invoke(CloseAll);
|
Dispatcher.Invoke(CloseAll);
|
||||||
|
@ -87,6 +93,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
|
||||||
});
|
});
|
||||||
|
|
||||||
result.Canceled = canceled;
|
result.Canceled = canceled;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,11 +181,5 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
|
||||||
autoResetEvent.Set();
|
autoResetEvent.Set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cancel()
|
|
||||||
{
|
|
||||||
canceled = true;
|
|
||||||
autoResetEvent.Set();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue