SEBWIN-313: Started implementing application blacklist mechanism.
This commit is contained in:
parent
51570ecc91
commit
b72c37273e
23 changed files with 620 additions and 128 deletions
|
@ -8,9 +8,11 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using SafeExamBrowser.Browser.Contracts;
|
using SafeExamBrowser.Browser.Contracts;
|
||||||
using SafeExamBrowser.Browser.Contracts.Events;
|
using SafeExamBrowser.Browser.Contracts.Events;
|
||||||
using SafeExamBrowser.Client.Contracts;
|
using SafeExamBrowser.Client.Contracts;
|
||||||
|
using SafeExamBrowser.Client.Operations.Events;
|
||||||
using SafeExamBrowser.Communication.Contracts.Data;
|
using SafeExamBrowser.Communication.Contracts.Data;
|
||||||
using SafeExamBrowser.Communication.Contracts.Events;
|
using SafeExamBrowser.Communication.Contracts.Events;
|
||||||
using SafeExamBrowser.Communication.Contracts.Hosts;
|
using SafeExamBrowser.Communication.Contracts.Hosts;
|
||||||
|
@ -343,7 +345,15 @@ namespace SafeExamBrowser.Client
|
||||||
|
|
||||||
private void Operations_ActionRequired(ActionRequiredEventArgs args)
|
private void Operations_ActionRequired(ActionRequiredEventArgs args)
|
||||||
{
|
{
|
||||||
// TODO
|
switch (args)
|
||||||
|
{
|
||||||
|
case ApplicationTerminationEventArgs a:
|
||||||
|
AskForAutomaticApplicationTermination(a);
|
||||||
|
break;
|
||||||
|
case ApplicationTerminationFailedEventArgs a:
|
||||||
|
InformAboutFailedApplicationTermination(a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Operations_ProgressChanged(ProgressChangedEventArgs args)
|
private void Operations_ProgressChanged(ProgressChangedEventArgs args)
|
||||||
|
@ -401,6 +411,27 @@ namespace SafeExamBrowser.Client
|
||||||
terminationActivator.Resume();
|
terminationActivator.Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AskForAutomaticApplicationTermination(ApplicationTerminationEventArgs args)
|
||||||
|
{
|
||||||
|
var nl = Environment.NewLine;
|
||||||
|
var applicationList = string.Join(Environment.NewLine, args.RunningApplications.Select(a => a.Name));
|
||||||
|
var warning = text.Get(TextKey.MessageBox_ApplicationAutoTerminationDataLossWarning);
|
||||||
|
var message = $"{text.Get(TextKey.MessageBox_ApplicationAutoTerminationQuestion)}{nl}{nl}{warning}{nl}{nl}{applicationList}";
|
||||||
|
var title = text.Get(TextKey.MessageBox_ApplicationAutoTerminationQuestionTitle);
|
||||||
|
var result = messageBox.Show(message, title, MessageBoxAction.YesNo, MessageBoxIcon.Question, parent: splashScreen);
|
||||||
|
|
||||||
|
args.TerminateProcesses = result == MessageBoxResult.Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InformAboutFailedApplicationTermination(ApplicationTerminationFailedEventArgs args)
|
||||||
|
{
|
||||||
|
var applicationList = string.Join(Environment.NewLine, args.Applications.Select(a => a.Name));
|
||||||
|
var message = $"{text.Get(TextKey.MessageBox_ApplicationTerminationFailure)}{Environment.NewLine}{Environment.NewLine}{applicationList}";
|
||||||
|
var title = text.Get(TextKey.MessageBox_ApplicationTerminationFailureTitle);
|
||||||
|
|
||||||
|
messageBox.Show(message, title, icon: MessageBoxIcon.Error, parent: splashScreen);
|
||||||
|
}
|
||||||
|
|
||||||
private bool TryInitiateShutdown()
|
private bool TryInitiateShutdown()
|
||||||
{
|
{
|
||||||
var hasQuitPassword = !String.IsNullOrEmpty(Settings.QuitPasswordHash);
|
var hasQuitPassword = !String.IsNullOrEmpty(Settings.QuitPasswordHash);
|
||||||
|
|
|
@ -29,7 +29,6 @@ using SafeExamBrowser.I18n.Contracts;
|
||||||
using SafeExamBrowser.Logging;
|
using SafeExamBrowser.Logging;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.Monitoring.Applications;
|
using SafeExamBrowser.Monitoring.Applications;
|
||||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
|
||||||
using SafeExamBrowser.Monitoring.Display;
|
using SafeExamBrowser.Monitoring.Display;
|
||||||
using SafeExamBrowser.Monitoring.Keyboard;
|
using SafeExamBrowser.Monitoring.Keyboard;
|
||||||
using SafeExamBrowser.Monitoring.Mouse;
|
using SafeExamBrowser.Monitoring.Mouse;
|
||||||
|
@ -63,7 +62,6 @@ namespace SafeExamBrowser.Client
|
||||||
private UserInterfaceMode uiMode;
|
private UserInterfaceMode uiMode;
|
||||||
|
|
||||||
private IActionCenter actionCenter;
|
private IActionCenter actionCenter;
|
||||||
private IApplicationMonitor applicationMonitor;
|
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private IMessageBox messageBox;
|
private IMessageBox messageBox;
|
||||||
private INativeMethods nativeMethods;
|
private INativeMethods nativeMethods;
|
||||||
|
@ -89,16 +87,16 @@ namespace SafeExamBrowser.Client
|
||||||
InitializeText();
|
InitializeText();
|
||||||
|
|
||||||
actionCenter = BuildActionCenter();
|
actionCenter = BuildActionCenter();
|
||||||
applicationMonitor = new ApplicationMonitor(new ModuleLogger(logger, nameof(ApplicationMonitor)), nativeMethods);
|
|
||||||
context = new ClientContext();
|
context = new ClientContext();
|
||||||
messageBox = BuildMessageBox();
|
messageBox = BuildMessageBox();
|
||||||
uiFactory = BuildUserInterfaceFactory();
|
uiFactory = BuildUserInterfaceFactory();
|
||||||
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)), Interlocutor.Client);
|
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client);
|
||||||
taskbar = BuildTaskbar();
|
taskbar = BuildTaskbar();
|
||||||
terminationActivator = new TerminationActivator(new ModuleLogger(logger, nameof(TerminationActivator)));
|
terminationActivator = new TerminationActivator(ModuleLogger(nameof(TerminationActivator)));
|
||||||
|
|
||||||
var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
var applicationMonitor = new ApplicationMonitor(ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, new ProcessFactory(ModuleLogger(nameof(ProcessFactory))));
|
||||||
var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods);
|
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
||||||
|
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
||||||
var hashAlgorithm = new HashAlgorithm();
|
var hashAlgorithm = new HashAlgorithm();
|
||||||
|
|
||||||
var operations = new Queue<IOperation>();
|
var operations = new Queue<IOperation>();
|
||||||
|
@ -195,7 +193,7 @@ namespace SafeExamBrowser.Client
|
||||||
|
|
||||||
private IOperation BuildBrowserOperation()
|
private IOperation BuildBrowserOperation()
|
||||||
{
|
{
|
||||||
var moduleLogger = new ModuleLogger(logger, nameof(BrowserApplication));
|
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
|
||||||
var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
|
var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
|
||||||
var browserInfo = new BrowserApplicationInfo();
|
var browserInfo = new BrowserApplicationInfo();
|
||||||
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, uiFactory);
|
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, uiFactory);
|
||||||
|
@ -209,7 +207,7 @@ namespace SafeExamBrowser.Client
|
||||||
{
|
{
|
||||||
var processId = Process.GetCurrentProcess().Id;
|
var processId = Process.GetCurrentProcess().Id;
|
||||||
var factory = new HostObjectFactory();
|
var factory = new HostObjectFactory();
|
||||||
var clientHost = new ClientHost(context.AppConfig.ClientAddress, factory, new ModuleLogger(logger, nameof(ClientHost)), processId, FIVE_SECONDS);
|
var clientHost = new ClientHost(context.AppConfig.ClientAddress, factory, ModuleLogger(nameof(ClientHost)), processId, FIVE_SECONDS);
|
||||||
var operation = new CommunicationHostOperation(clientHost, logger);
|
var operation = new CommunicationHostOperation(clientHost, logger);
|
||||||
|
|
||||||
context.ClientHost = clientHost;
|
context.ClientHost = clientHost;
|
||||||
|
@ -220,7 +218,7 @@ namespace SafeExamBrowser.Client
|
||||||
|
|
||||||
private IOperation BuildKeyboardInterceptorOperation()
|
private IOperation BuildKeyboardInterceptorOperation()
|
||||||
{
|
{
|
||||||
var keyboardInterceptor = new KeyboardInterceptor(new ModuleLogger(logger, nameof(KeyboardInterceptor)), nativeMethods, context.Settings.Keyboard);
|
var keyboardInterceptor = new KeyboardInterceptor(ModuleLogger(nameof(KeyboardInterceptor)), nativeMethods, context.Settings.Keyboard);
|
||||||
var operation = new KeyboardInterceptorOperation(context, keyboardInterceptor, logger);
|
var operation = new KeyboardInterceptorOperation(context, keyboardInterceptor, logger);
|
||||||
|
|
||||||
return operation;
|
return operation;
|
||||||
|
@ -228,7 +226,7 @@ namespace SafeExamBrowser.Client
|
||||||
|
|
||||||
private IOperation BuildMouseInterceptorOperation()
|
private IOperation BuildMouseInterceptorOperation()
|
||||||
{
|
{
|
||||||
var mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, nameof(MouseInterceptor)), nativeMethods, context.Settings.Mouse);
|
var mouseInterceptor = new MouseInterceptor(ModuleLogger(nameof(MouseInterceptor)), nativeMethods, context.Settings.Mouse);
|
||||||
var operation = new MouseInterceptorOperation(context, logger, mouseInterceptor);
|
var operation = new MouseInterceptorOperation(context, logger, mouseInterceptor);
|
||||||
|
|
||||||
return operation;
|
return operation;
|
||||||
|
@ -238,16 +236,16 @@ namespace SafeExamBrowser.Client
|
||||||
{
|
{
|
||||||
var aboutInfo = new AboutNotificationInfo(text);
|
var aboutInfo = new AboutNotificationInfo(text);
|
||||||
var aboutController = new AboutNotificationController(context.AppConfig, uiFactory);
|
var aboutController = new AboutNotificationController(context.AppConfig, uiFactory);
|
||||||
var audio = new Audio(context.Settings.Audio, new ModuleLogger(logger, nameof(Audio)));
|
var audio = new Audio(context.Settings.Audio, ModuleLogger(nameof(Audio)));
|
||||||
var keyboard = new Keyboard(new ModuleLogger(logger, nameof(Keyboard)));
|
var keyboard = new Keyboard(ModuleLogger(nameof(Keyboard)));
|
||||||
var logInfo = new LogNotificationInfo(text);
|
var logInfo = new LogNotificationInfo(text);
|
||||||
var logController = new LogNotificationController(logger, uiFactory);
|
var logController = new LogNotificationController(logger, uiFactory);
|
||||||
var powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply)));
|
var powerSupply = new PowerSupply(ModuleLogger(nameof(PowerSupply)));
|
||||||
var wirelessAdapter = new WirelessAdapter(new ModuleLogger(logger, nameof(WirelessAdapter)));
|
var wirelessAdapter = new WirelessAdapter(ModuleLogger(nameof(WirelessAdapter)));
|
||||||
var activators = new IActionCenterActivator[]
|
var activators = new IActionCenterActivator[]
|
||||||
{
|
{
|
||||||
new KeyboardActivator(new ModuleLogger(logger, nameof(KeyboardActivator))),
|
new KeyboardActivator(ModuleLogger(nameof(KeyboardActivator))),
|
||||||
new TouchActivator(new ModuleLogger(logger, nameof(TouchActivator)))
|
new TouchActivator(ModuleLogger(nameof(TouchActivator)))
|
||||||
};
|
};
|
||||||
var operation = new ShellOperation(
|
var operation = new ShellOperation(
|
||||||
actionCenter,
|
actionCenter,
|
||||||
|
@ -298,9 +296,9 @@ namespace SafeExamBrowser.Client
|
||||||
switch (uiMode)
|
switch (uiMode)
|
||||||
{
|
{
|
||||||
case UserInterfaceMode.Mobile:
|
case UserInterfaceMode.Mobile:
|
||||||
return new Mobile.Taskbar(new ModuleLogger(logger, nameof(Mobile.Taskbar)));
|
return new Mobile.Taskbar(ModuleLogger(nameof(Mobile.Taskbar)));
|
||||||
default:
|
default:
|
||||||
return new Desktop.Taskbar(new ModuleLogger(logger, nameof(Desktop.Taskbar)));
|
return new Desktop.Taskbar(ModuleLogger(nameof(Desktop.Taskbar)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,5 +317,10 @@ namespace SafeExamBrowser.Client
|
||||||
{
|
{
|
||||||
ClientController.UpdateAppConfig();
|
ClientController.UpdateAppConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IModuleLogger ModuleLogger(string moduleInfo)
|
||||||
|
{
|
||||||
|
return new ModuleLogger(logger, moduleInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace SafeExamBrowser.Client.Operations
|
||||||
public override OperationResult Perform()
|
public override OperationResult Perform()
|
||||||
{
|
{
|
||||||
logger.Info("Initializing applications...");
|
logger.Info("Initializing applications...");
|
||||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeProcessMonitoring);
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeApplications);
|
||||||
|
|
||||||
var result = InitializeApplications();
|
var result = InitializeApplications();
|
||||||
|
|
||||||
|
@ -51,9 +51,9 @@ namespace SafeExamBrowser.Client.Operations
|
||||||
public override OperationResult Revert()
|
public override OperationResult Revert()
|
||||||
{
|
{
|
||||||
logger.Info("Finalizing applications...");
|
logger.Info("Finalizing applications...");
|
||||||
StatusChanged?.Invoke(TextKey.OperationStatus_StopProcessMonitoring);
|
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeApplications);
|
||||||
|
|
||||||
TerminateApplications();
|
FinalizeApplications();
|
||||||
StopMonitor();
|
StopMonitor();
|
||||||
|
|
||||||
return OperationResult.Success;
|
return OperationResult.Success;
|
||||||
|
@ -64,25 +64,48 @@ namespace SafeExamBrowser.Client.Operations
|
||||||
var initialization = applicationMonitor.Initialize(Context.Settings.Applications);
|
var initialization = applicationMonitor.Initialize(Context.Settings.Applications);
|
||||||
var result = OperationResult.Success;
|
var result = OperationResult.Success;
|
||||||
|
|
||||||
if (initialization.RunningApplications.Any())
|
if (initialization.FailedAutoTerminations.Any())
|
||||||
|
{
|
||||||
|
result = HandleAutoTerminationFailure(initialization.FailedAutoTerminations);
|
||||||
|
}
|
||||||
|
else if (initialization.RunningApplications.Any())
|
||||||
{
|
{
|
||||||
result = TryTerminate(initialization.RunningApplications);
|
result = TryTerminate(initialization.RunningApplications);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == OperationResult.Success)
|
if (result == OperationResult.Success)
|
||||||
{
|
{
|
||||||
foreach (var application in Context.Settings.Applications.Whitelist)
|
CreateApplications();
|
||||||
{
|
|
||||||
Create(application);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateApplications()
|
||||||
|
{
|
||||||
|
foreach (var application in Context.Settings.Applications.Whitelist)
|
||||||
|
{
|
||||||
|
Create(application);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Create(WhitelistApplication application)
|
private void Create(WhitelistApplication application)
|
||||||
{
|
{
|
||||||
// TODO: Use IApplicationFactory to create new application according to configuration, load into Context.Applications
|
// TODO: Use IApplicationFactory to create new application according to configuration, load into Context.Applications
|
||||||
|
// StatusChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FinalizeApplications()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperationResult HandleAutoTerminationFailure(IList<RunningApplication> applications)
|
||||||
|
{
|
||||||
|
logger.Error($"{applications.Count} application(s) could not be automatically terminated: {string.Join(", ", applications.Select(a => a.Name))}");
|
||||||
|
ActionRequired?.Invoke(new ApplicationTerminationFailedEventArgs(applications));
|
||||||
|
|
||||||
|
return OperationResult.Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartMonitor()
|
private void StartMonitor()
|
||||||
|
@ -101,35 +124,42 @@ namespace SafeExamBrowser.Client.Operations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TerminateApplications()
|
private OperationResult TryTerminate(IEnumerable<RunningApplication> runningApplications)
|
||||||
{
|
{
|
||||||
|
var args = new ApplicationTerminationEventArgs(runningApplications);
|
||||||
}
|
var failed = new List<RunningApplication>();
|
||||||
|
|
||||||
private OperationResult TryTerminate(IEnumerable<RunningApplicationInfo> runningApplications)
|
|
||||||
{
|
|
||||||
var args = new ProcessTerminationEventArgs();
|
|
||||||
var result = OperationResult.Success;
|
var result = OperationResult.Success;
|
||||||
|
|
||||||
ActionRequired?.Invoke(args);
|
ActionRequired?.Invoke(args);
|
||||||
|
|
||||||
if (args.TerminateProcesses)
|
if (args.TerminateProcesses)
|
||||||
{
|
{
|
||||||
// TODO: Terminate all processes of all running applications
|
foreach (var application in runningApplications)
|
||||||
|
{
|
||||||
|
var success = applicationMonitor.TryTerminate(application);
|
||||||
|
|
||||||
//foreach (var application in runningApplications)
|
if (success)
|
||||||
//{
|
{
|
||||||
// foreach (var process in application.Processes)
|
logger.Info($"Successfully terminated application '{application.Name}'.");
|
||||||
// {
|
}
|
||||||
// process.Kill();
|
else
|
||||||
// }
|
{
|
||||||
//}
|
result = OperationResult.Failed;
|
||||||
|
failed.Add(application);
|
||||||
|
logger.Error($"Failed to automatically terminate application '{application.Name}'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = OperationResult.Aborted;
|
result = OperationResult.Aborted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (failed.Any())
|
||||||
|
{
|
||||||
|
ActionRequired?.Invoke(new ApplicationTerminationFailedEventArgs(failed));
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* 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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||||
|
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Client.Operations.Events
|
||||||
|
{
|
||||||
|
internal class ApplicationTerminationEventArgs : ActionRequiredEventArgs
|
||||||
|
{
|
||||||
|
internal IEnumerable<RunningApplication> RunningApplications { get; }
|
||||||
|
internal bool TerminateProcesses { get; set; }
|
||||||
|
|
||||||
|
internal ApplicationTerminationEventArgs(IEnumerable<RunningApplication> runningApplications)
|
||||||
|
{
|
||||||
|
RunningApplications = runningApplications;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,12 +6,19 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||||
|
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Client.Operations.Events
|
namespace SafeExamBrowser.Client.Operations.Events
|
||||||
{
|
{
|
||||||
internal class ProcessTerminationEventArgs : ActionRequiredEventArgs
|
internal class ApplicationTerminationFailedEventArgs : ActionRequiredEventArgs
|
||||||
{
|
{
|
||||||
public bool TerminateProcesses { get; set; }
|
internal IEnumerable<RunningApplication> Applications { get; }
|
||||||
|
|
||||||
|
internal ApplicationTerminationFailedEventArgs(IEnumerable<RunningApplication> applications)
|
||||||
|
{
|
||||||
|
Applications = applications;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -76,7 +76,8 @@
|
||||||
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
|
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
|
||||||
<Compile Include="Operations\ClientOperation.cs" />
|
<Compile Include="Operations\ClientOperation.cs" />
|
||||||
<Compile Include="Operations\ConfigurationOperation.cs" />
|
<Compile Include="Operations\ConfigurationOperation.cs" />
|
||||||
<Compile Include="Operations\Events\ProcessTerminationEventArgs.cs" />
|
<Compile Include="Operations\Events\ApplicationTerminationEventArgs.cs" />
|
||||||
|
<Compile Include="Operations\Events\ApplicationTerminationFailedEventArgs.cs" />
|
||||||
<Compile Include="Operations\RuntimeConnectionOperation.cs" />
|
<Compile Include="Operations\RuntimeConnectionOperation.cs" />
|
||||||
<Compile Include="Communication\ClientHost.cs" />
|
<Compile Include="Communication\ClientHost.cs" />
|
||||||
<Compile Include="CompositionRoot.cs" />
|
<Compile Include="CompositionRoot.cs" />
|
||||||
|
|
|
@ -22,8 +22,13 @@ namespace SafeExamBrowser.I18n.Contracts
|
||||||
BrowserWindow_ZoomMenuItem,
|
BrowserWindow_ZoomMenuItem,
|
||||||
Build,
|
Build,
|
||||||
LogWindow_Title,
|
LogWindow_Title,
|
||||||
|
MessageBox_ApplicationAutoTerminationDataLossWarning,
|
||||||
|
MessageBox_ApplicationAutoTerminationQuestion,
|
||||||
|
MessageBox_ApplicationAutoTerminationQuestionTitle,
|
||||||
MessageBox_ApplicationError,
|
MessageBox_ApplicationError,
|
||||||
MessageBox_ApplicationErrorTitle,
|
MessageBox_ApplicationErrorTitle,
|
||||||
|
MessageBox_ApplicationTerminationFailure,
|
||||||
|
MessageBox_ApplicationTerminationFailureTitle,
|
||||||
MessageBox_BrowserNavigationBlocked,
|
MessageBox_BrowserNavigationBlocked,
|
||||||
MessageBox_BrowserNavigationBlockedTitle,
|
MessageBox_BrowserNavigationBlockedTitle,
|
||||||
MessageBox_CancelButton,
|
MessageBox_CancelButton,
|
||||||
|
@ -70,15 +75,15 @@ namespace SafeExamBrowser.I18n.Contracts
|
||||||
Notification_LogTooltip,
|
Notification_LogTooltip,
|
||||||
OperationStatus_CloseRuntimeConnection,
|
OperationStatus_CloseRuntimeConnection,
|
||||||
OperationStatus_EmptyClipboard,
|
OperationStatus_EmptyClipboard,
|
||||||
|
OperationStatus_FinalizeApplications,
|
||||||
OperationStatus_FinalizeServiceSession,
|
OperationStatus_FinalizeServiceSession,
|
||||||
|
OperationStatus_InitializeApplications,
|
||||||
OperationStatus_InitializeBrowser,
|
OperationStatus_InitializeBrowser,
|
||||||
OperationStatus_InitializeConfiguration,
|
OperationStatus_InitializeConfiguration,
|
||||||
OperationStatus_InitializeKioskMode,
|
OperationStatus_InitializeKioskMode,
|
||||||
OperationStatus_InitializeProcessMonitoring,
|
|
||||||
OperationStatus_InitializeRuntimeConnection,
|
OperationStatus_InitializeRuntimeConnection,
|
||||||
OperationStatus_InitializeServiceSession,
|
OperationStatus_InitializeServiceSession,
|
||||||
OperationStatus_InitializeShell,
|
OperationStatus_InitializeShell,
|
||||||
OperationStatus_InitializeWindowMonitoring,
|
|
||||||
OperationStatus_InitializeWorkingArea,
|
OperationStatus_InitializeWorkingArea,
|
||||||
OperationStatus_RestartCommunicationHost,
|
OperationStatus_RestartCommunicationHost,
|
||||||
OperationStatus_RestoreWorkingArea,
|
OperationStatus_RestoreWorkingArea,
|
||||||
|
@ -92,8 +97,6 @@ namespace SafeExamBrowser.I18n.Contracts
|
||||||
OperationStatus_StopCommunicationHost,
|
OperationStatus_StopCommunicationHost,
|
||||||
OperationStatus_StopKeyboardInterception,
|
OperationStatus_StopKeyboardInterception,
|
||||||
OperationStatus_StopMouseInterception,
|
OperationStatus_StopMouseInterception,
|
||||||
OperationStatus_StopProcessMonitoring,
|
|
||||||
OperationStatus_StopWindowMonitoring,
|
|
||||||
OperationStatus_TerminateBrowser,
|
OperationStatus_TerminateBrowser,
|
||||||
OperationStatus_TerminateShell,
|
OperationStatus_TerminateShell,
|
||||||
OperationStatus_WaitExplorerStartup,
|
OperationStatus_WaitExplorerStartup,
|
||||||
|
|
|
@ -24,12 +24,27 @@
|
||||||
<Entry key="LogWindow_Title">
|
<Entry key="LogWindow_Title">
|
||||||
Application Log
|
Application Log
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="MessageBox_ApplicationAutoTerminationQuestion">
|
||||||
|
The applications listed below need to be terminated before a new session can be started. Would you like to automatically terminate them now?
|
||||||
|
</Entry>
|
||||||
|
<Entry key="MessageBox_ApplicationAutoTerminationQuestionTitle">
|
||||||
|
Running Applications Detected
|
||||||
|
</Entry>
|
||||||
|
<Entry key="MessageBox_ApplicationAutoTerminationDataLossWarning">
|
||||||
|
IMPORTANT: Any unsaved application data might be lost!
|
||||||
|
</Entry>
|
||||||
<Entry key="MessageBox_ApplicationError">
|
<Entry key="MessageBox_ApplicationError">
|
||||||
An unrecoverable error has occurred! Please consult the application log for more information. The application will now shut down...
|
An unrecoverable error has occurred! Please consult the application log for more information. The application will now shut down...
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="MessageBox_ApplicationErrorTitle">
|
<Entry key="MessageBox_ApplicationErrorTitle">
|
||||||
Application Error
|
Application Error
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="MessageBox_ApplicationTerminationFailure">
|
||||||
|
The applications listed below could not be terminated! Please terminate them manually and try again...
|
||||||
|
</Entry>
|
||||||
|
<Entry key="MessageBox_ApplicationTerminationFailureTitle">
|
||||||
|
Automatic Termination Failed
|
||||||
|
</Entry>
|
||||||
<Entry key="MessageBox_BrowserNavigationBlocked">
|
<Entry key="MessageBox_BrowserNavigationBlocked">
|
||||||
Access to "%%URL%%" is not allowed according to the application configuration.
|
Access to "%%URL%%" is not allowed according to the application configuration.
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -168,9 +183,15 @@
|
||||||
<Entry key="OperationStatus_EmptyClipboard">
|
<Entry key="OperationStatus_EmptyClipboard">
|
||||||
Emptying clipboard
|
Emptying clipboard
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="OperationStatus_FinalizeApplications">
|
||||||
|
Finalizing applications
|
||||||
|
</Entry>
|
||||||
<Entry key="OperationStatus_FinalizeServiceSession">
|
<Entry key="OperationStatus_FinalizeServiceSession">
|
||||||
Finalizing service session
|
Finalizing service session
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="OperationStatus_InitializeApplications">
|
||||||
|
Initializing applications
|
||||||
|
</Entry>
|
||||||
<Entry key="OperationStatus_InitializeBrowser">
|
<Entry key="OperationStatus_InitializeBrowser">
|
||||||
Initializing browser
|
Initializing browser
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -180,9 +201,6 @@
|
||||||
<Entry key="OperationStatus_InitializeKioskMode">
|
<Entry key="OperationStatus_InitializeKioskMode">
|
||||||
Initializing kiosk mode
|
Initializing kiosk mode
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="OperationStatus_InitializeProcessMonitoring">
|
|
||||||
Initializing process monitoring
|
|
||||||
</Entry>
|
|
||||||
<Entry key="OperationStatus_InitializeRuntimeConnection">
|
<Entry key="OperationStatus_InitializeRuntimeConnection">
|
||||||
Initializing runtime connection
|
Initializing runtime connection
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -195,9 +213,6 @@
|
||||||
<Entry key="OperationStatus_InitializeShell">
|
<Entry key="OperationStatus_InitializeShell">
|
||||||
Initializing user interface
|
Initializing user interface
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="OperationStatus_InitializeWindowMonitoring">
|
|
||||||
Initializing window monitoring
|
|
||||||
</Entry>
|
|
||||||
<Entry key="OperationStatus_InitializeWorkingArea">
|
<Entry key="OperationStatus_InitializeWorkingArea">
|
||||||
Initializing working area
|
Initializing working area
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -234,12 +249,6 @@
|
||||||
<Entry key="OperationStatus_StopMouseInterception">
|
<Entry key="OperationStatus_StopMouseInterception">
|
||||||
Stopping mouse interception
|
Stopping mouse interception
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry key="OperationStatus_StopProcessMonitoring">
|
|
||||||
Stopping process monitoring
|
|
||||||
</Entry>
|
|
||||||
<Entry key="OperationStatus_StopWindowMonitoring">
|
|
||||||
Stopping window monitoring
|
|
||||||
</Entry>
|
|
||||||
<Entry key="OperationStatus_TerminateBrowser">
|
<Entry key="OperationStatus_TerminateBrowser">
|
||||||
Terminating browser
|
Terminating browser
|
||||||
</Entry>
|
</Entry>
|
||||||
|
|
|
@ -35,5 +35,10 @@ namespace SafeExamBrowser.Monitoring.Contracts.Applications
|
||||||
/// Stops the application monitoring.
|
/// Stops the application monitoring.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to terminate all processes of the specified application. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||||
|
/// </summary>
|
||||||
|
bool TryTerminate(RunningApplication application);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,20 @@ namespace SafeExamBrowser.Monitoring.Contracts.Applications
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class InitializationResult
|
public class InitializationResult
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A list of currently running applications which could not be automatically terminated.
|
||||||
|
/// </summary>
|
||||||
|
public IList<RunningApplication> FailedAutoTerminations { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of currently running applications which need to be terminated.
|
/// A list of currently running applications which need to be terminated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<RunningApplicationInfo> RunningApplications { get; }
|
public IList<RunningApplication> RunningApplications { get; }
|
||||||
|
|
||||||
public InitializationResult()
|
public InitializationResult()
|
||||||
{
|
{
|
||||||
RunningApplications = new List<RunningApplicationInfo>();
|
FailedAutoTerminations = new List<RunningApplication>();
|
||||||
|
RunningApplications = new List<RunningApplication>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,22 @@ namespace SafeExamBrowser.Monitoring.Contracts.Applications
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides information about a running application.
|
/// Provides information about a running application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RunningApplicationInfo
|
public class RunningApplication
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the application.
|
/// The name of the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of processes which belong to the application.
|
/// A list of processes which belong to the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<IProcess> Processes { get; set; }
|
public IList<IProcess> Processes { get; }
|
||||||
|
|
||||||
|
public RunningApplication(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Processes = new List<IProcess>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -53,7 +53,7 @@
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Applications\RunningApplicationInfo.cs" />
|
<Compile Include="Applications\RunningApplication.cs" />
|
||||||
<Compile Include="Display\Events\DisplayChangedEventHandler.cs" />
|
<Compile Include="Display\Events\DisplayChangedEventHandler.cs" />
|
||||||
<Compile Include="Applications\Events\ExplorerStartedEventHandler.cs" />
|
<Compile Include="Applications\Events\ExplorerStartedEventHandler.cs" />
|
||||||
<Compile Include="Display\IDisplayMonitor.cs" />
|
<Compile Include="Display\IDisplayMonitor.cs" />
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
|
using System.Threading;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||||
using SafeExamBrowser.Monitoring.Contracts.Applications.Events;
|
using SafeExamBrowser.Monitoring.Contracts.Applications.Events;
|
||||||
|
@ -20,34 +22,73 @@ namespace SafeExamBrowser.Monitoring.Applications
|
||||||
public class ApplicationMonitor : IApplicationMonitor
|
public class ApplicationMonitor : IApplicationMonitor
|
||||||
{
|
{
|
||||||
private IntPtr activeWindow;
|
private IntPtr activeWindow;
|
||||||
|
private IList<BlacklistApplication> blacklist;
|
||||||
private Guid? captureHookId;
|
private Guid? captureHookId;
|
||||||
|
private ManagementEventWatcher explorerWatcher;
|
||||||
private Guid? foregroundHookId;
|
private Guid? foregroundHookId;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private INativeMethods nativeMethods;
|
private INativeMethods nativeMethods;
|
||||||
private ManagementEventWatcher explorerWatcher;
|
private IProcessFactory processFactory;
|
||||||
|
private IList<WhitelistApplication> whitelist;
|
||||||
|
|
||||||
public event ExplorerStartedEventHandler ExplorerStarted;
|
public event ExplorerStartedEventHandler ExplorerStarted;
|
||||||
|
|
||||||
public ApplicationMonitor(ILogger logger, INativeMethods nativeMethods)
|
public ApplicationMonitor(ILogger logger, INativeMethods nativeMethods, IProcessFactory processFactory)
|
||||||
{
|
{
|
||||||
|
this.blacklist = new List<BlacklistApplication>();
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.nativeMethods = nativeMethods;
|
this.nativeMethods = nativeMethods;
|
||||||
|
this.processFactory = processFactory;
|
||||||
|
this.whitelist = new List<WhitelistApplication>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InitializationResult Initialize(ApplicationSettings settings)
|
public InitializationResult Initialize(ApplicationSettings settings)
|
||||||
{
|
{
|
||||||
// TODO
|
var result = new InitializationResult();
|
||||||
// Initialize blacklist
|
|
||||||
// Initialize whitelist
|
|
||||||
// Check for running processes
|
|
||||||
|
|
||||||
return new InitializationResult();
|
foreach (var application in settings.Blacklist)
|
||||||
|
{
|
||||||
|
blacklist.Add(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var application in settings.Whitelist)
|
||||||
|
{
|
||||||
|
whitelist.Add(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug($"Initialized blacklist with {blacklist.Count} applications: {string.Join(", ", blacklist.Select(a => a.ExecutableName))}");
|
||||||
|
logger.Debug($"Initialized whitelist with {whitelist.Count} applications: {string.Join(", ", whitelist.Select(a => a.ExecutableName))}");
|
||||||
|
|
||||||
|
foreach (var process in processFactory.GetAllRunning())
|
||||||
|
{
|
||||||
|
foreach (var application in blacklist)
|
||||||
|
{
|
||||||
|
var isMatch = BelongsToApplication(process, application);
|
||||||
|
|
||||||
|
if (isMatch && !application.AutoTerminate)
|
||||||
|
{
|
||||||
|
AddForTermination(application.ExecutableName, process, result);
|
||||||
|
}
|
||||||
|
else if (isMatch && application.AutoTerminate && !TryTerminate(process))
|
||||||
|
{
|
||||||
|
AddFailed(application.ExecutableName, process, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var application in whitelist)
|
||||||
|
{
|
||||||
|
// TODO: Check if application is running, auto-terminate or add to result.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
// TODO: Start monitoring blacklist...
|
// TODO: Start monitoring blacklist...
|
||||||
|
|
||||||
|
// TODO: Remove WMI event and use timer mechanism!
|
||||||
explorerWatcher = new ManagementEventWatcher(@"\\.\root\CIMV2", GetQueryFor("explorer.exe"));
|
explorerWatcher = new ManagementEventWatcher(@"\\.\root\CIMV2", GetQueryFor("explorer.exe"));
|
||||||
explorerWatcher.EventArrived += new EventArrivedEventHandler(ExplorerWatcher_EventArrived);
|
explorerWatcher.EventArrived += new EventArrivedEventHandler(ExplorerWatcher_EventArrived);
|
||||||
explorerWatcher.Start();
|
explorerWatcher.Start();
|
||||||
|
@ -78,9 +119,52 @@ namespace SafeExamBrowser.Monitoring.Applications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Terminate(int processId)
|
public bool TryTerminate(RunningApplication application)
|
||||||
{
|
{
|
||||||
return false;
|
var success = true;
|
||||||
|
|
||||||
|
foreach (var process in application.Processes)
|
||||||
|
{
|
||||||
|
success &= TryTerminate(process);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddFailed(string name, IProcess process, InitializationResult result)
|
||||||
|
{
|
||||||
|
var application = result.FailedAutoTerminations.FirstOrDefault(a => a.Name == name);
|
||||||
|
|
||||||
|
if (application == default(RunningApplication))
|
||||||
|
{
|
||||||
|
application = new RunningApplication(name);
|
||||||
|
result.FailedAutoTerminations.Add(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
application.Processes.Add(process);
|
||||||
|
logger.Error($"Process '{process.Name}' belongs to application '{application.Name}' and could not be terminated automatically!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddForTermination(string name, IProcess process, InitializationResult result)
|
||||||
|
{
|
||||||
|
var application = result.RunningApplications.FirstOrDefault(a => a.Name == name);
|
||||||
|
|
||||||
|
if (application == default(RunningApplication))
|
||||||
|
{
|
||||||
|
application = new RunningApplication(name);
|
||||||
|
result.RunningApplications.Add(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
application.Processes.Add(process);
|
||||||
|
logger.Debug($"Process '{process.Name}' belongs to application '{application.Name}' and needs to be terminated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool BelongsToApplication(IProcess process, BlacklistApplication application)
|
||||||
|
{
|
||||||
|
var sameName = process.Name.Equals(application.ExecutableName, StringComparison.OrdinalIgnoreCase);
|
||||||
|
var sameOriginalName = process.OriginalName?.Equals(application.ExecutableOriginalName, StringComparison.OrdinalIgnoreCase) == true;
|
||||||
|
|
||||||
|
return sameName || sameOriginalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Check(IntPtr window)
|
private void Check(IntPtr window)
|
||||||
|
@ -109,19 +193,20 @@ namespace SafeExamBrowser.Monitoring.Applications
|
||||||
private bool IsAllowed(IntPtr window)
|
private bool IsAllowed(IntPtr window)
|
||||||
{
|
{
|
||||||
var processId = nativeMethods.GetProcessIdFor(window);
|
var processId = nativeMethods.GetProcessIdFor(window);
|
||||||
var process = Process.GetProcessById(Convert.ToInt32(processId));
|
// TODO: Allow only if in whitelist!
|
||||||
|
//var process = processFactory.GetById(Convert.ToInt32(processId));
|
||||||
|
|
||||||
if (process != null)
|
//if (process != null)
|
||||||
{
|
//{
|
||||||
var allowed = process.ProcessName == "SafeExamBrowser" || process.ProcessName == "SafeExamBrowser.Client";
|
// var allowed = process.Name == "SafeExamBrowser" || process.Name == "SafeExamBrowser.Client";
|
||||||
|
|
||||||
if (!allowed)
|
// if (!allowed)
|
||||||
{
|
// {
|
||||||
logger.Warn($"Window with handle = {window} belongs to not allowed process '{process.ProcessName}'!");
|
// logger.Warn($"Window with handle = {window} belongs to not allowed process '{process.Name}'!");
|
||||||
}
|
// }
|
||||||
|
|
||||||
return allowed;
|
// return allowed;
|
||||||
}
|
//}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +228,57 @@ namespace SafeExamBrowser.Monitoring.Applications
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryTerminate(IProcess process)
|
||||||
|
{
|
||||||
|
const int MAX_ATTEMPTS = 5;
|
||||||
|
const int TIMEOUT = 100;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (var attempt = 0; attempt < MAX_ATTEMPTS; attempt++)
|
||||||
|
{
|
||||||
|
if (process.TryClose())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Thread.Sleep(TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.HasTerminated)
|
||||||
|
{
|
||||||
|
for (var attempt = 0; attempt < MAX_ATTEMPTS; attempt++)
|
||||||
|
{
|
||||||
|
if (process.TryKill())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Thread.Sleep(TIMEOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.HasTerminated)
|
||||||
|
{
|
||||||
|
logger.Info($"Successfully terminated process '{process.Name}'.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn($"Failed to terminate process '{process.Name}'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"An error occurred while attempting to terminate process '{process.Name}'!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.HasTerminated;
|
||||||
|
}
|
||||||
|
|
||||||
private void ExplorerWatcher_EventArrived(object sender, EventArrivedEventArgs e)
|
private void ExplorerWatcher_EventArrived(object sender, EventArrivedEventArgs e)
|
||||||
{
|
{
|
||||||
var eventName = e.NewEvent.ClassPath.ClassName;
|
var eventName = e.NewEvent.ClassPath.ClassName;
|
||||||
|
|
|
@ -186,7 +186,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
|
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
|
||||||
proxy.Verify(p => p.Disconnect(), Times.Once);
|
proxy.Verify(p => p.Disconnect(), Times.Once);
|
||||||
process.Verify(p => p.Kill(), Times.Never);
|
process.Verify(p => p.TryKill(), Times.Never);
|
||||||
|
|
||||||
Assert.IsNull(sessionContext.ClientProcess);
|
Assert.IsNull(sessionContext.ClientProcess);
|
||||||
Assert.IsNull(sessionContext.ClientProxy);
|
Assert.IsNull(sessionContext.ClientProxy);
|
||||||
|
@ -195,12 +195,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Revert_MustKillClientIfStoppingFailed()
|
public void Revert_MustKillClientIfStoppingFailed()
|
||||||
{
|
{
|
||||||
process.Setup(p => p.Kill()).Callback(() => process.SetupGet(p => p.HasTerminated).Returns(true));
|
process.Setup(p => p.TryKill()).Callback(() => process.SetupGet(p => p.HasTerminated).Returns(true));
|
||||||
|
|
||||||
PerformNormally();
|
PerformNormally();
|
||||||
sut.Revert();
|
sut.Revert();
|
||||||
|
|
||||||
process.Verify(p => p.Kill(), Times.AtLeastOnce);
|
process.Verify(p => p.TryKill(), Times.AtLeastOnce);
|
||||||
|
|
||||||
Assert.IsNull(sessionContext.ClientProcess);
|
Assert.IsNull(sessionContext.ClientProcess);
|
||||||
Assert.IsNull(sessionContext.ClientProxy);
|
Assert.IsNull(sessionContext.ClientProxy);
|
||||||
|
@ -212,7 +212,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
PerformNormally();
|
PerformNormally();
|
||||||
sut.Revert();
|
sut.Revert();
|
||||||
|
|
||||||
process.Verify(p => p.Kill(), Times.Exactly(5));
|
process.Verify(p => p.TryKill(), Times.Exactly(5));
|
||||||
|
|
||||||
Assert.IsNotNull(sessionContext.ClientProcess);
|
Assert.IsNotNull(sessionContext.ClientProcess);
|
||||||
Assert.IsNotNull(sessionContext.ClientProxy);
|
Assert.IsNotNull(sessionContext.ClientProxy);
|
||||||
|
@ -227,7 +227,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
proxy.Verify(p => p.InitiateShutdown(), Times.Never);
|
proxy.Verify(p => p.InitiateShutdown(), Times.Never);
|
||||||
proxy.Verify(p => p.Disconnect(), Times.Never);
|
proxy.Verify(p => p.Disconnect(), Times.Never);
|
||||||
process.Verify(p => p.Kill(), Times.Never);
|
process.Verify(p => p.TryKill(), Times.Never);
|
||||||
|
|
||||||
Assert.IsNull(sessionContext.ClientProcess);
|
Assert.IsNull(sessionContext.ClientProcess);
|
||||||
Assert.IsNull(sessionContext.ClientProxy);
|
Assert.IsNull(sessionContext.ClientProxy);
|
||||||
|
|
|
@ -81,7 +81,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
|
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
|
||||||
proxy.Verify(p => p.Disconnect(), Times.Once);
|
proxy.Verify(p => p.Disconnect(), Times.Once);
|
||||||
process.Verify(p => p.Kill(), Times.Never);
|
process.Verify(p => p.TryKill(), Times.Never);
|
||||||
|
|
||||||
Assert.IsNull(sessionContext.ClientProcess);
|
Assert.IsNull(sessionContext.ClientProcess);
|
||||||
Assert.IsNull(sessionContext.ClientProxy);
|
Assert.IsNull(sessionContext.ClientProxy);
|
||||||
|
|
|
@ -240,32 +240,30 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryKillClient(int attempt = 0)
|
private bool TryKillClient()
|
||||||
{
|
{
|
||||||
const int MAX_ATTEMPTS = 5;
|
const int MAX_ATTEMPTS = 5;
|
||||||
|
|
||||||
if (attempt == MAX_ATTEMPTS)
|
for (var attempt = 1; attempt <= MAX_ATTEMPTS; attempt++)
|
||||||
{
|
{
|
||||||
logger.Error($"Failed to kill client process within {MAX_ATTEMPTS} attempts!");
|
logger.Info($"Attempt {attempt}/{MAX_ATTEMPTS} to kill client process with ID = {ClientProcess.Id}.");
|
||||||
|
|
||||||
return false;
|
if (ClientProcess.TryKill())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info($"Killing client process with ID = {ClientProcess.Id}.");
|
|
||||||
ClientProcess.Kill();
|
|
||||||
|
|
||||||
if (ClientProcess.HasTerminated)
|
if (ClientProcess.HasTerminated)
|
||||||
{
|
{
|
||||||
logger.Info("Client process has terminated.");
|
logger.Info("Client process has terminated.");
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.Warn("Failed to kill client process. Trying again...");
|
logger.Error($"Failed to kill client process within {MAX_ATTEMPTS} attempts!");
|
||||||
|
|
||||||
return TryKillClient(++attempt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ClientProcess.HasTerminated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,18 +12,18 @@ using System.Collections.Generic;
|
||||||
namespace SafeExamBrowser.Settings.Applications
|
namespace SafeExamBrowser.Settings.Applications
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TODO
|
/// Defines all settings for third-party applications and application monitoring.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ApplicationSettings
|
public class ApplicationSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// All applications which are not allowed to run during a session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<BlacklistApplication> Blacklist { get; set; }
|
public IList<BlacklistApplication> Blacklist { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// All applications which are allowed to run during a session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<WhitelistApplication> Whitelist { get; set; }
|
public IList<WhitelistApplication> Whitelist { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,14 @@ using System;
|
||||||
namespace SafeExamBrowser.Settings.Applications
|
namespace SafeExamBrowser.Settings.Applications
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TODO
|
/// Defines an application which is whitelisted, i.e. allowed to run during a session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class WhitelistApplication
|
public class WhitelistApplication
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the main executable of the application.
|
||||||
|
/// </summary>
|
||||||
|
public string ExecutableName { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,29 @@ namespace SafeExamBrowser.WindowsApi.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int Id { get; }
|
int Id { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The file name of the process executable.
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The original file name of the process executable, if available.
|
||||||
|
/// </summary>
|
||||||
|
string OriginalName { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event fired when the process has terminated.
|
/// Event fired when the process has terminated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event ProcessTerminatedEventHandler Terminated;
|
event ProcessTerminatedEventHandler Terminated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Immediately terminates the process.
|
/// Attempts to gracefully terminate the process by closing its main window. This will only work for interactive processes which have a main window.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Kill();
|
bool TryClose();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to immediately kill the process.
|
||||||
|
/// </summary>
|
||||||
|
bool TryKill();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace SafeExamBrowser.WindowsApi.Contracts
|
namespace SafeExamBrowser.WindowsApi.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -19,10 +21,20 @@ namespace SafeExamBrowser.WindowsApi.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDesktop StartupDesktop { set; }
|
IDesktop StartupDesktop { set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves all currently running processes.
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<IProcess> GetAllRunning();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts a new process with the given command-line arguments.
|
/// Starts a new process with the given command-line arguments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="System.ComponentModel.Win32Exception">If the process could not be started.</exception>
|
/// <exception cref="System.ComponentModel.Win32Exception">If the process could not be started.</exception>
|
||||||
IProcess StartNew(string path, params string[] args);
|
IProcess StartNew(string path, params string[] args);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to retrieve a process by its identifier. Returns <c>true</c> if a process was found, otherwise <c>false</c>.
|
||||||
|
/// </summary>
|
||||||
|
bool TryGetById(int id, out IProcess process);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,12 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.WindowsApi.Contracts;
|
using SafeExamBrowser.WindowsApi.Contracts;
|
||||||
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
using SafeExamBrowser.WindowsApi.Contracts.Events;
|
||||||
|
|
||||||
|
@ -13,9 +19,12 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
{
|
{
|
||||||
internal class Process : IProcess
|
internal class Process : IProcess
|
||||||
{
|
{
|
||||||
|
private bool eventInitialized, originalNameInitialized;
|
||||||
|
private ILogger logger;
|
||||||
|
private string originalName;
|
||||||
private System.Diagnostics.Process process;
|
private System.Diagnostics.Process process;
|
||||||
|
|
||||||
public event ProcessTerminatedEventHandler Terminated;
|
private event ProcessTerminatedEventHandler TerminatedEvent;
|
||||||
|
|
||||||
public int Id
|
public int Id
|
||||||
{
|
{
|
||||||
|
@ -24,29 +33,141 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
|
|
||||||
public bool HasTerminated
|
public bool HasTerminated
|
||||||
{
|
{
|
||||||
get { process.Refresh(); return process.HasExited; }
|
get { return IsTerminated(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public Process(int id)
|
public string Name { get; }
|
||||||
|
|
||||||
|
public string OriginalName
|
||||||
{
|
{
|
||||||
process = System.Diagnostics.Process.GetProcessById(id);
|
get { return originalNameInitialized ? originalName : InitializeOriginalName(); }
|
||||||
process.Exited += Process_Exited;
|
|
||||||
process.EnableRaisingEvents = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Kill()
|
public event ProcessTerminatedEventHandler Terminated
|
||||||
{
|
{
|
||||||
process.Refresh();
|
add { TerminatedEvent += value; InitializeEvent(); }
|
||||||
|
remove { TerminatedEvent -= value; }
|
||||||
|
}
|
||||||
|
|
||||||
if (!process.HasExited)
|
internal Process(System.Diagnostics.Process process, ILogger logger)
|
||||||
|
{
|
||||||
|
this.Name = process.ProcessName;
|
||||||
|
this.process = process;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Process(System.Diagnostics.Process process, string originalName, ILogger logger) : this(process, logger)
|
||||||
|
{
|
||||||
|
this.originalName = originalName;
|
||||||
|
this.originalNameInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryClose()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
process.Kill();
|
process.Refresh();
|
||||||
|
|
||||||
|
if (!process.HasExited)
|
||||||
|
{
|
||||||
|
process.CloseMainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.HasExited;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to close main window!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryKill()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
process.Refresh();
|
||||||
|
|
||||||
|
if (!process.HasExited)
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.HasExited;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to kill process!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsTerminated()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
process.Refresh();
|
||||||
|
|
||||||
|
return process.HasExited;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to check whether process is terminated!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeEvent()
|
||||||
|
{
|
||||||
|
if (!eventInitialized)
|
||||||
|
{
|
||||||
|
eventInitialized = true;
|
||||||
|
process.Exited += Process_Exited;
|
||||||
|
process.EnableRaisingEvents = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Process_Exited(object sender, System.EventArgs e)
|
private string InitializeOriginalName()
|
||||||
{
|
{
|
||||||
Terminated?.Invoke(process.ExitCode);
|
try
|
||||||
|
{
|
||||||
|
using (var searcher = new ManagementObjectSearcher($"SELECT ExecutablePath FROM Win32_Process WHERE ProcessId = {process.Id}"))
|
||||||
|
using (var results = searcher.Get())
|
||||||
|
using (var processData = results.Cast<ManagementObject>().First())
|
||||||
|
{
|
||||||
|
var executablePath = Convert.ToString(processData["ExecutablePath"]);
|
||||||
|
|
||||||
|
if (File.Exists(executablePath))
|
||||||
|
{
|
||||||
|
var executableInfo = FileVersionInfo.GetVersionInfo(executablePath);
|
||||||
|
var originalName = Path.GetFileNameWithoutExtension(executableInfo.OriginalFilename);
|
||||||
|
|
||||||
|
this.originalName = originalName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn("Could not find original name!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to initialize original name!", e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
originalNameInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Process_Exited(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
TerminatedEvent?.Invoke(process.ExitCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,27 +7,44 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.WindowsApi.Contracts;
|
|
||||||
using SafeExamBrowser.WindowsApi.Constants;
|
using SafeExamBrowser.WindowsApi.Constants;
|
||||||
|
using SafeExamBrowser.WindowsApi.Contracts;
|
||||||
using SafeExamBrowser.WindowsApi.Types;
|
using SafeExamBrowser.WindowsApi.Types;
|
||||||
|
|
||||||
namespace SafeExamBrowser.WindowsApi
|
namespace SafeExamBrowser.WindowsApi
|
||||||
{
|
{
|
||||||
public class ProcessFactory : IProcessFactory
|
public class ProcessFactory : IProcessFactory
|
||||||
{
|
{
|
||||||
private ILogger logger;
|
private IModuleLogger logger;
|
||||||
|
|
||||||
public IDesktop StartupDesktop { private get; set; }
|
public IDesktop StartupDesktop { private get; set; }
|
||||||
|
|
||||||
public ProcessFactory(ILogger logger)
|
public ProcessFactory(IModuleLogger logger)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IProcess> GetAllRunning()
|
||||||
|
{
|
||||||
|
var processes = System.Diagnostics.Process.GetProcesses();
|
||||||
|
var originalNames = LoadOriginalNames();
|
||||||
|
|
||||||
|
foreach (var process in processes)
|
||||||
|
{
|
||||||
|
var originalName = originalNames.FirstOrDefault(n => n.processId == process.Id).originalName;
|
||||||
|
|
||||||
|
yield return new Process(process, originalName, LoggerFor(process));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IProcess StartNew(string path, params string[] args)
|
public IProcess StartNew(string path, params string[] args)
|
||||||
{
|
{
|
||||||
var commandLine = $"{'"' + path + '"'} {String.Join(" ", args)}";
|
var commandLine = $"{'"' + path + '"'} {String.Join(" ", args)}";
|
||||||
|
@ -48,11 +65,73 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||||
}
|
}
|
||||||
|
|
||||||
var process = new Process(processInfo.dwProcessId);
|
var raw = System.Diagnostics.Process.GetProcessById(processInfo.dwProcessId);
|
||||||
|
var process = new Process(raw, LoggerFor(raw));
|
||||||
|
|
||||||
logger.Info($"Successfully started process '{Path.GetFileName(path)}' with ID = {process.Id}.");
|
logger.Info($"Successfully started process '{Path.GetFileName(path)}' with ID = {process.Id}.");
|
||||||
|
|
||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetById(int id, out IProcess process)
|
||||||
|
{
|
||||||
|
var raw = System.Diagnostics.Process.GetProcesses().FirstOrDefault(p => p.Id == id);
|
||||||
|
|
||||||
|
process = default(IProcess);
|
||||||
|
|
||||||
|
if (raw != default(System.Diagnostics.Process))
|
||||||
|
{
|
||||||
|
process = new Process(raw, LoggerFor(raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
return process != default(IProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<(int processId, string originalName)> LoadOriginalNames()
|
||||||
|
{
|
||||||
|
var names = new List<(int, string)>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var searcher = new ManagementObjectSearcher($"SELECT Name, ProcessId, ExecutablePath FROM Win32_Process"))
|
||||||
|
using (var results = searcher.Get())
|
||||||
|
{
|
||||||
|
var processData = results.Cast<ManagementObject>().ToList();
|
||||||
|
|
||||||
|
foreach (var process in processData)
|
||||||
|
{
|
||||||
|
using (process)
|
||||||
|
{
|
||||||
|
var processId = Convert.ToInt32(process["ProcessId"]);
|
||||||
|
var processName = Convert.ToString(process["Name"]);
|
||||||
|
var executablePath = Convert.ToString(process["ExecutablePath"]);
|
||||||
|
|
||||||
|
if (File.Exists(executablePath))
|
||||||
|
{
|
||||||
|
var executableInfo = FileVersionInfo.GetVersionInfo(executablePath);
|
||||||
|
var originalName = Path.GetFileNameWithoutExtension(executableInfo.OriginalFilename);
|
||||||
|
|
||||||
|
names.Add((processId, originalName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
names.Add((processId, default(string)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error("Failed to retrieve original names for processes!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ILogger LoggerFor(System.Diagnostics.Process process)
|
||||||
|
{
|
||||||
|
return logger.CloneFor($"{nameof(Process)} '{process.ProcessName}' ({process.Id})");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Management" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Constants\Constant.cs" />
|
<Compile Include="Constants\Constant.cs" />
|
||||||
|
|
Loading…
Add table
Reference in a new issue