SEBWIN-312: Started implementing application factory and initialization of whitelisted applications.

This commit is contained in:
dbuechel 2019-11-05 10:08:19 +01:00
parent 2b976e8150
commit 7e76b029a6
26 changed files with 517 additions and 29 deletions

View file

@ -0,0 +1,31 @@
/*
* 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/.
*/
namespace SafeExamBrowser.Applications.Contracts
{
/// <summary>
/// Defines all possible results of an attempt to create an application.
/// </summary>
public enum FactoryResult
{
/// <summary>
/// An error occurred while trying to create the application.
/// </summary>
Error,
/// <summary>
/// The application could not be found on the system.
/// </summary>
NotFound,
/// <summary>
/// The application has been created successfully.
/// </summary>
Success
}
}

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.Applications.Contracts.Events;
namespace SafeExamBrowser.Applications.Contracts
{
/// <summary>
/// Controls the lifetime and functionality of a (third-party) application.
/// Controls the lifetime and functionality of an application.
/// </summary>
public interface IApplication
{

View file

@ -0,0 +1,23 @@
/*
* 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 SafeExamBrowser.Settings.Applications;
namespace SafeExamBrowser.Applications.Contracts
{
/// <summary>
/// Provides functionality to create external applications.
/// </summary>
public interface IApplicationFactory
{
/// <summary>
/// Attempts to create an application according to the given settings.
/// </summary>
FactoryResult TryCreate(WhitelistApplication settings, out IApplication application);
}
}

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.Core.Contracts;
namespace SafeExamBrowser.Applications.Contracts
{
/// <summary>
/// The information about a (third-party) application which can be accessed via the shell.
/// The information about an application which can be accessed via the shell.
/// </summary>
public interface IApplicationInfo
{

View file

@ -7,12 +7,11 @@
*/
using SafeExamBrowser.Applications.Contracts.Events;
using SafeExamBrowser.Core.Contracts;
namespace SafeExamBrowser.Applications.Contracts
{
/// <summary>
/// Defines an instance of a (third-party) application which can be accessed via the shell.
/// Defines an instance of an application which can be accessed via the shell.
/// </summary>
public interface IApplicationInstance
{

View file

@ -9,7 +9,7 @@
namespace SafeExamBrowser.Applications.Contracts
{
/// <summary>
/// Defines an identifier which uniquely identifies an instance in the context of a (third-party) application.
/// Defines an identifier which uniquely identifies an instance in the context of an application.
/// </summary>
public abstract class InstanceIdentifier
{

View file

@ -57,7 +57,9 @@
<Compile Include="Events\InstanceStartedEventHandler.cs" />
<Compile Include="Events\InstanceTerminatedEventHandler.cs" />
<Compile Include="Events\NameChangedEventHandler.cs" />
<Compile Include="FactoryResult.cs" />
<Compile Include="IApplication.cs" />
<Compile Include="IApplicationFactory.cs" />
<Compile Include="IApplicationInfo.cs" />
<Compile Include="IApplicationInstance.cs" />
<Compile Include="InstanceIdentifier.cs" />
@ -68,6 +70,10 @@
<Project>{fe0e1224-b447-4b14-81e7-ed7d84822aa0}</Project>
<Name>SafeExamBrowser.Core.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
<Name>SafeExamBrowser.Settings</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,128 @@
/*
* 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;
using System.Collections.Generic;
using System.IO;
using Microsoft.Win32;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings.Applications;
namespace SafeExamBrowser.Applications
{
public class ApplicationFactory : IApplicationFactory
{
private ILogger logger;
public ApplicationFactory(ILogger logger)
{
this.logger = logger;
}
public FactoryResult TryCreate(WhitelistApplication settings, out IApplication application)
{
application = default(IApplication);
try
{
var success = TryFindMainExecutable(settings, out var mainExecutable);
if (success)
{
application = new ExternalApplication();
logger.Debug($"Successfully initialized application '{settings.DisplayName}' ({settings.ExecutableName}).");
return FactoryResult.Success;
}
logger.Error($"Could not find application '{settings.DisplayName}' ({settings.ExecutableName})!");
return FactoryResult.NotFound;
}
catch (Exception e)
{
logger.Error($"Unexpected error while trying to create application '{settings.DisplayName}' ({settings.ExecutableName})!", e);
}
return FactoryResult.Error;
}
private bool TryFindMainExecutable(WhitelistApplication settings, out string mainExecutable)
{
var paths = new List<string[]>();
var registryPath = QueryPathFromRegistry(settings);
mainExecutable = default(string);
paths.Add(new[] { "%ProgramW6432%", settings.ExecutableName });
paths.Add(new[] { Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), settings.ExecutableName });
paths.Add(new[] { Environment.GetFolderPath(Environment.SpecialFolder.System), settings.ExecutableName });
paths.Add(new[] { Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), settings.ExecutableName });
if (settings.ExecutablePath != default(string))
{
paths.Add(new[] { settings.ExecutablePath, settings.ExecutableName });
paths.Add(new[] { "%ProgramW6432%", settings.ExecutablePath, settings.ExecutableName });
paths.Add(new[] { Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), settings.ExecutablePath, settings.ExecutableName });
paths.Add(new[] { Environment.GetFolderPath(Environment.SpecialFolder.System), settings.ExecutablePath, settings.ExecutableName });
paths.Add(new[] { Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), settings.ExecutablePath, settings.ExecutableName });
}
if (registryPath != default(string))
{
paths.Add(new[] { registryPath, settings.ExecutableName });
if (settings.ExecutablePath != default(string))
{
paths.Add(new[] { registryPath, settings.ExecutablePath, settings.ExecutableName });
}
}
foreach (var path in paths)
{
try
{
mainExecutable = Path.Combine(path);
mainExecutable = Environment.ExpandEnvironmentVariables(mainExecutable);
if (File.Exists(mainExecutable))
{
return true;
}
}
catch (Exception e)
{
logger.Error($"Failed to test path {string.Join(@"\", path)}!", e);
}
}
return false;
}
private string QueryPathFromRegistry(WhitelistApplication settings)
{
try
{
using (var key = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\{settings.ExecutableName}"))
{
if (key != null)
{
return key.GetValue("Path") as string;
}
}
}
catch (Exception e)
{
logger.Error($"Failed to query path in registry for '{settings.ExecutableName}'!", e);
}
return default(string);
}
}
}

View file

@ -0,0 +1,36 @@
/*
* 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;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Applications.Contracts.Events;
namespace SafeExamBrowser.Applications
{
internal class ExternalApplication : IApplication
{
public IApplicationInfo Info => throw new NotImplementedException();
public event InstanceStartedEventHandler InstanceStarted;
public void Initialize()
{
}
public void Start()
{
}
public void Terminate()
{
}
}
}

View file

@ -0,0 +1,33 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SafeExamBrowser.Applications")]
[assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.Applications")]
[assembly: AssemblyCopyright("Copyright © 2019 ETH Zürich, Educational Development and Technology (LET)")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a113e68f-1209-4689-981a-15c554b2df4e")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A113E68F-1209-4689-981A-15C554B2DF4E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SafeExamBrowser.Applications</RootNamespace>
<AssemblyName>SafeExamBrowser.Applications</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="ApplicationFactory.cs" />
<Compile Include="ExternalApplication.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Applications.Contracts\SafeExamBrowser.Applications.Contracts.csproj">
<Project>{ac77745d-3b41-43e2-8e84-d40e5a4ee77f}</Project>
<Name>SafeExamBrowser.Applications.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
<Name>SafeExamBrowser.Logging.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
<Name>SafeExamBrowser.Settings</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Configuration.Contracts;
@ -26,6 +27,11 @@ namespace SafeExamBrowser.Client
/// </summary>
internal IList<IActionCenterActivator> Activators { get; }
/// <summary>
/// All applications allowed for the current session.
/// </summary>
internal IList<IApplication> Applications { get; }
/// <summary>
/// The global application configuration.
/// </summary>
@ -54,6 +60,7 @@ namespace SafeExamBrowser.Client
internal ClientContext()
{
Activators = new List<IActionCenterActivator>();
Applications = new List<IApplication>();
}
}
}

View file

@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Browser.Contracts.Events;
using SafeExamBrowser.Client.Contracts;
@ -409,6 +410,12 @@ namespace SafeExamBrowser.Client
{
switch (args)
{
case ApplicationNotFoundEventArgs a:
AskForApplicationPath(a);
break;
case ApplicationInitializationFailedEventArgs a:
InformAboutFailedApplicationInitialization(a);
break;
case ApplicationTerminationEventArgs a:
AskForAutomaticApplicationTermination(a);
break;
@ -485,6 +492,30 @@ namespace SafeExamBrowser.Client
args.TerminateProcesses = result == MessageBoxResult.Yes;
}
private void AskForApplicationPath(ApplicationNotFoundEventArgs args)
{
// TODO
}
private void InformAboutFailedApplicationInitialization(ApplicationInitializationFailedEventArgs args)
{
var messageKey = TextKey.MessageBox_ApplicationInitializationFailure;
var titleKey = TextKey.MessageBox_ApplicationInitializationFailureTitle;
switch (args.Result)
{
case FactoryResult.NotFound:
messageKey = TextKey.MessageBox_ApplicationNotFound;
titleKey = TextKey.MessageBox_ApplicationNotFoundTitle;
break;
}
var message = text.Get(messageKey).Replace("%%NAME%%", $"'{args.DisplayName}' ({args.ExecutableName})");
var title = text.Get(titleKey);
messageBox.Show(message, title, icon: MessageBoxIcon.Error, parent: splashScreen);
}
private void InformAboutFailedApplicationTermination(ApplicationTerminationFailedEventArgs args)
{
var applicationList = string.Join(Environment.NewLine, args.Applications.Select(a => a.Name));

View file

@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using SafeExamBrowser.Applications;
using SafeExamBrowser.Browser;
using SafeExamBrowser.Client.Communication;
using SafeExamBrowser.Client.Contracts;
@ -95,6 +96,7 @@ namespace SafeExamBrowser.Client
taskbar = BuildTaskbar();
terminationActivator = new TerminationActivator(ModuleLogger(nameof(TerminationActivator)));
var applicationFactory = new ApplicationFactory(ModuleLogger(nameof(ApplicationFactory)));
var applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, new ProcessFactory(ModuleLogger(nameof(ProcessFactory))));
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
@ -110,7 +112,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS));
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new ApplicationOperation(applicationMonitor, context, logger));
operations.Enqueue(new ApplicationOperation(context, applicationFactory, applicationMonitor, logger, text));
operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar));
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));

View file

@ -8,6 +8,7 @@
using System.Collections.Generic;
using System.Linq;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Client.Operations.Events;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
@ -21,16 +22,25 @@ namespace SafeExamBrowser.Client.Operations
{
internal class ApplicationOperation : ClientOperation
{
private IApplicationFactory factory;
private ILogger logger;
private IApplicationMonitor applicationMonitor;
private IApplicationMonitor monitor;
private IText text;
public override event ActionRequiredEventHandler ActionRequired;
public override event StatusChangedEventHandler StatusChanged;
public ApplicationOperation(IApplicationMonitor applicationMonitor, ClientContext context, ILogger logger) : base(context)
public ApplicationOperation(
ClientContext context,
IApplicationFactory factory,
IApplicationMonitor monitor,
ILogger logger,
IText text) : base(context)
{
this.applicationMonitor = applicationMonitor;
this.factory = factory;
this.monitor = monitor;
this.logger = logger;
this.text = text;
}
public override OperationResult Perform()
@ -61,7 +71,7 @@ namespace SafeExamBrowser.Client.Operations
private OperationResult InitializeApplications()
{
var initialization = applicationMonitor.Initialize(Context.Settings.Applications);
var initialization = monitor.Initialize(Context.Settings.Applications);
var result = OperationResult.Success;
if (initialization.FailedAutoTerminations.Any())
@ -75,24 +85,46 @@ namespace SafeExamBrowser.Client.Operations
if (result == OperationResult.Success)
{
CreateApplications();
foreach (var application in Context.Settings.Applications.Whitelist)
{
Initialize(application);
}
}
return result;
}
private void CreateApplications()
private void Initialize(WhitelistApplication settings)
{
foreach (var application in Context.Settings.Applications.Whitelist)
{
Create(application);
}
}
var result = factory.TryCreate(settings, out var application);
private void Create(WhitelistApplication application)
{
// TODO: Use IApplicationFactory to create new application according to configuration, load into Context.Applications
// StatusChanged?.Invoke();
while (result == FactoryResult.NotFound && settings.AllowCustomPath)
{
var args = new ApplicationNotFoundEventArgs(settings.DisplayName, settings.ExecutableName);
ActionRequired?.Invoke(args);
if (args.Success)
{
settings.ExecutablePath = args.CustomPath;
result = factory.TryCreate(settings, out application);
}
else
{
break;
}
}
if (result == FactoryResult.Success)
{
application.Initialize();
Context.Applications.Add(application);
}
else
{
logger.Error($"Failed to initialize application '{settings.DisplayName}' ({settings.ExecutableName}). Reason: {result}.");
ActionRequired?.Invoke(new ApplicationInitializationFailedEventArgs(settings.DisplayName, settings.ExecutableName, result));
}
}
private void FinalizeApplications()
@ -112,7 +144,7 @@ namespace SafeExamBrowser.Client.Operations
{
if (Context.Settings.KioskMode != KioskMode.None)
{
applicationMonitor.Start();
monitor.Start();
}
}
@ -120,7 +152,7 @@ namespace SafeExamBrowser.Client.Operations
{
if (Context.Settings.KioskMode != KioskMode.None)
{
applicationMonitor.Stop();
monitor.Stop();
}
}
@ -139,7 +171,7 @@ namespace SafeExamBrowser.Client.Operations
foreach (var application in runningApplications)
{
var success = applicationMonitor.TryTerminate(application);
var success = monitor.TryTerminate(application);
if (success)
{

View file

@ -0,0 +1,27 @@
/*
* 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 SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
namespace SafeExamBrowser.Client.Operations.Events
{
internal class ApplicationInitializationFailedEventArgs : ActionRequiredEventArgs
{
internal string DisplayName { get; }
internal string ExecutableName { get; }
internal FactoryResult Result { get; }
internal ApplicationInitializationFailedEventArgs(string displayName, string executableName, FactoryResult result)
{
DisplayName = displayName;
ExecutableName = executableName;
Result = result;
}
}
}

View file

@ -0,0 +1,26 @@
/*
* 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 SafeExamBrowser.Core.Contracts.OperationModel.Events;
namespace SafeExamBrowser.Client.Operations.Events
{
internal class ApplicationNotFoundEventArgs : ActionRequiredEventArgs
{
internal string CustomPath { get; set; }
internal string DisplayName { get; }
internal string ExecutableName { get; }
internal bool Success { get; set; }
internal ApplicationNotFoundEventArgs(string displayName, string executableName)
{
DisplayName = displayName;
ExecutableName = executableName;
}
}
}

View file

@ -76,6 +76,8 @@
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
<Compile Include="Operations\ClientOperation.cs" />
<Compile Include="Operations\ConfigurationOperation.cs" />
<Compile Include="Operations\Events\ApplicationNotFoundEventArgs.cs" />
<Compile Include="Operations\Events\ApplicationInitializationFailedEventArgs.cs" />
<Compile Include="Operations\Events\ApplicationTerminationEventArgs.cs" />
<Compile Include="Operations\Events\ApplicationTerminationFailedEventArgs.cs" />
<Compile Include="Operations\RuntimeConnectionOperation.cs" />
@ -127,6 +129,10 @@
<Project>{ac77745d-3b41-43e2-8e84-d40e5a4ee77f}</Project>
<Name>SafeExamBrowser.Applications.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Applications\SafeExamBrowser.Applications.csproj">
<Project>{a113e68f-1209-4689-981a-15c554b2df4e}</Project>
<Name>SafeExamBrowser.Applications</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Browser.Contracts\SafeExamBrowser.Browser.Contracts.csproj">
<Project>{5fb5273d-277c-41dd-8593-a25ce1aff2e9}</Project>
<Name>SafeExamBrowser.Browser.Contracts</Name>

View file

@ -32,6 +32,10 @@ namespace SafeExamBrowser.I18n.Contracts
MessageBox_ApplicationAutoTerminationQuestionTitle,
MessageBox_ApplicationError,
MessageBox_ApplicationErrorTitle,
MessageBox_ApplicationInitializationFailure,
MessageBox_ApplicationInitializationFailureTitle,
MessageBox_ApplicationNotFound,
MessageBox_ApplicationNotFoundTitle,
MessageBox_ApplicationTerminationFailure,
MessageBox_ApplicationTerminationFailureTitle,
MessageBox_BrowserNavigationBlocked,

View file

@ -54,6 +54,18 @@
<Entry key="MessageBox_ApplicationErrorTitle">
Application Error
</Entry>
<Entry key="MessageBox_ApplicationInitializationFailure">
Application %%NAME%% could not be initialized and will thus not be available for the new session! Please consult the log files for more information.
</Entry>
<Entry key="MessageBox_ApplicationInitializationFailureTitle">
Application Initialization Failed
</Entry>
<Entry key="MessageBox_ApplicationNotFound">
Application %%NAME%% could not be found on the system and will thus not be available for the new session! Please consult the log files for more information.
</Entry>
<Entry key="MessageBox_ApplicationNotFoundTitle">
Application Not Found
</Entry>
<Entry key="MessageBox_ApplicationTerminationFailure">
The applications listed below could not be terminated! Please terminate them manually and try again...
</Entry>

View file

@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Lockdown.Contracts
{
/// <summary>
/// Allows to control a feature of the computer, the operating system or an installed third-party software.
/// Allows to control a feature of the computer, the operating system or an installed software.
/// </summary>
public interface IFeatureConfiguration
{

View file

@ -39,7 +39,7 @@ namespace SafeExamBrowser.Settings
public bool AllowApplicationLogAccess { get; set; }
/// <summary>
/// All settings related to third-party applications.
/// All settings related to external applications.
/// </summary>
public ApplicationSettings Applications { get; set; }

View file

@ -12,7 +12,7 @@ using System.Collections.Generic;
namespace SafeExamBrowser.Settings.Applications
{
/// <summary>
/// Defines all settings for third-party applications and application monitoring.
/// Defines all settings for external applications and application monitoring.
/// </summary>
[Serializable]
public class ApplicationSettings

View file

@ -9,7 +9,7 @@
namespace SafeExamBrowser.UserInterface.Contracts.Shell
{
/// <summary>
/// The control for a (third-party) application which can be loaded into the shell.
/// The control for an application which can be loaded into the shell.
/// </summary>
public interface IApplicationControl
{

View file

@ -11,7 +11,7 @@ using System.Collections.Generic;
namespace SafeExamBrowser.WindowsApi.Contracts
{
/// <summary>
/// The factory for processes, to be used whenever a new process needs to be created (for internal components and third-party applications).
/// The factory for processes, to be used whenever a new process needs to be created (for internal components and external applications).
/// </summary>
public interface IProcessFactory
{

View file

@ -106,6 +106,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Settings",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Browser.UnitTests", "SafeExamBrowser.Browser.UnitTests\SafeExamBrowser.Browser.UnitTests.csproj", "{F54C4C0E-4C72-4F88-A389-7F6DE3CCB745}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Applications", "SafeExamBrowser.Applications\SafeExamBrowser.Applications.csproj", "{A113E68F-1209-4689-981A-15C554B2DF4E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -476,6 +478,14 @@ Global
{F54C4C0E-4C72-4F88-A389-7F6DE3CCB745}.Release|Any CPU.Build.0 = Release|Any CPU
{F54C4C0E-4C72-4F88-A389-7F6DE3CCB745}.Release|x86.ActiveCfg = Release|x86
{F54C4C0E-4C72-4F88-A389-7F6DE3CCB745}.Release|x86.Build.0 = Release|x86
{A113E68F-1209-4689-981A-15C554B2DF4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A113E68F-1209-4689-981A-15C554B2DF4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A113E68F-1209-4689-981A-15C554B2DF4E}.Debug|x86.ActiveCfg = Debug|x86
{A113E68F-1209-4689-981A-15C554B2DF4E}.Debug|x86.Build.0 = Debug|x86
{A113E68F-1209-4689-981A-15C554B2DF4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A113E68F-1209-4689-981A-15C554B2DF4E}.Release|Any CPU.Build.0 = Release|Any CPU
{A113E68F-1209-4689-981A-15C554B2DF4E}.Release|x86.ActiveCfg = Release|x86
{A113E68F-1209-4689-981A-15C554B2DF4E}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE