SEBSP-61: Implemented basic metadata collection & transmission.
This commit is contained in:
		
							parent
							
								
									cb81906945
								
							
						
					
					
						commit
						731a748552
					
				
					 46 changed files with 557 additions and 132 deletions
				
			
		|  | @ -16,7 +16,7 @@ namespace SafeExamBrowser.Applications.Contracts | ||||||
| 	/// <summary> | 	/// <summary> | ||||||
| 	/// Controls the lifetime and functionality of an application. | 	/// Controls the lifetime and functionality of an application. | ||||||
| 	/// </summary> | 	/// </summary> | ||||||
| 	public interface IApplication | 	public interface IApplication<out TWindow> where TWindow : IApplicationWindow | ||||||
| 	{ | 	{ | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Indicates whether the application should be automatically started. | 		/// Indicates whether the application should be automatically started. | ||||||
|  | @ -51,7 +51,7 @@ namespace SafeExamBrowser.Applications.Contracts | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Returns all windows of the application. | 		/// Returns all windows of the application. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		IEnumerable<IApplicationWindow> GetWindows(); | 		IEnumerable<TWindow> GetWindows(); | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Performs any initialization work, if necessary. | 		/// Performs any initialization work, if necessary. | ||||||
|  |  | ||||||
|  | @ -18,6 +18,6 @@ namespace SafeExamBrowser.Applications.Contracts | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Attempts to create an application according to the given settings. | 		/// Attempts to create an application according to the given settings. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		FactoryResult TryCreate(WhitelistApplication settings, out IApplication application); | 		FactoryResult TryCreate(WhitelistApplication settings, out IApplication<IApplicationWindow> application); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ using SafeExamBrowser.Core.Contracts.Resources.Icons; | ||||||
| namespace SafeExamBrowser.Applications.Contracts | namespace SafeExamBrowser.Applications.Contracts | ||||||
| { | { | ||||||
| 	/// <summary> | 	/// <summary> | ||||||
| 	/// Defines a window of an <see cref="IApplication"/>. | 	/// Defines a window of an <see cref="IApplication{TWindow}"/>. | ||||||
| 	/// </summary> | 	/// </summary> | ||||||
| 	public interface IApplicationWindow | 	public interface IApplicationWindow | ||||||
| 	{ | 	{ | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ namespace SafeExamBrowser.Applications | ||||||
| 			this.registry = registry; | 			this.registry = registry; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public FactoryResult TryCreate(WhitelistApplication settings, out IApplication application) | 		public FactoryResult TryCreate(WhitelistApplication settings, out IApplication<IApplicationWindow> application) | ||||||
| 		{ | 		{ | ||||||
| 			var name = $"'{settings.DisplayName}' ({settings.ExecutableName})"; | 			var name = $"'{settings.DisplayName}' ({settings.ExecutableName})"; | ||||||
| 
 | 
 | ||||||
|  | @ -72,7 +72,7 @@ namespace SafeExamBrowser.Applications | ||||||
| 			return FactoryResult.Error; | 			return FactoryResult.Error; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private IApplication BuildApplication(string executablePath, WhitelistApplication settings) | 		private IApplication<IApplicationWindow> BuildApplication(string executablePath, WhitelistApplication settings) | ||||||
| 		{ | 		{ | ||||||
| 			const int ONE_SECOND = 1000; | 			const int ONE_SECOND = 1000; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ using SafeExamBrowser.WindowsApi.Contracts; | ||||||
| 
 | 
 | ||||||
| namespace SafeExamBrowser.Applications | namespace SafeExamBrowser.Applications | ||||||
| { | { | ||||||
| 	internal class ExternalApplication : IApplication | 	internal class ExternalApplication : IApplication<IApplicationWindow> | ||||||
| 	{ | 	{ | ||||||
| 		private readonly object @lock = new object(); | 		private readonly object @lock = new object(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ namespace SafeExamBrowser.Browser.Contracts | ||||||
| 	/// <summary> | 	/// <summary> | ||||||
| 	/// Controls the lifetime and functionality of the browser application. | 	/// Controls the lifetime and functionality of the browser application. | ||||||
| 	/// </summary> | 	/// </summary> | ||||||
| 	public interface IBrowserApplication : IApplication | 	public interface IBrowserApplication : IApplication<IBrowserWindow> | ||||||
| 	{ | 	{ | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Event fired when the browser application detects a download request for an application configuration file. | 		/// Event fired when the browser application detects a download request for an application configuration file. | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								SafeExamBrowser.Browser.Contracts/IBrowserWindow.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								SafeExamBrowser.Browser.Contracts/IBrowserWindow.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2023 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; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Browser.Contracts | ||||||
|  | { | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Defines a window of the <see cref="IBrowserApplication"/>. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public interface IBrowserWindow : IApplicationWindow | ||||||
|  | 	{ | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Indicates whether the window is the main browser window. | ||||||
|  | 		/// </summary> | ||||||
|  | 		bool IsMainWindow { get; } | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// The currently loaded URL, or <c>default(string)</c> in case no navigation has happened yet. | ||||||
|  | 		/// </summary> | ||||||
|  | 		string Url { get; } | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -67,6 +67,7 @@ | ||||||
|     <Compile Include="Filters\IRuleFactory.cs" /> |     <Compile Include="Filters\IRuleFactory.cs" /> | ||||||
|     <Compile Include="Filters\Request.cs" /> |     <Compile Include="Filters\Request.cs" /> | ||||||
|     <Compile Include="IBrowserApplication.cs" /> |     <Compile Include="IBrowserApplication.cs" /> | ||||||
|  |     <Compile Include="IBrowserWindow.cs" /> | ||||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> |     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|  |  | ||||||
|  | @ -14,7 +14,6 @@ using System.Linq; | ||||||
| using System.Threading; | using System.Threading; | ||||||
| using CefSharp; | using CefSharp; | ||||||
| using CefSharp.WinForms; | using CefSharp.WinForms; | ||||||
| using SafeExamBrowser.Applications.Contracts; |  | ||||||
| using SafeExamBrowser.Applications.Contracts.Events; | using SafeExamBrowser.Applications.Contracts.Events; | ||||||
| using SafeExamBrowser.Browser.Contracts; | using SafeExamBrowser.Browser.Contracts; | ||||||
| using SafeExamBrowser.Browser.Contracts.Events; | using SafeExamBrowser.Browser.Contracts.Events; | ||||||
|  | @ -99,9 +98,9 @@ namespace SafeExamBrowser.Browser | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public IEnumerable<IApplicationWindow> GetWindows() | 		public IEnumerable<IBrowserWindow> GetWindows() | ||||||
| 		{ | 		{ | ||||||
| 			return new List<IApplicationWindow>(windows); | 			return new List<IBrowserWindow>(windows); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public void Initialize() | 		public void Initialize() | ||||||
|  |  | ||||||
|  | @ -13,7 +13,6 @@ using System.Threading.Tasks; | ||||||
| using CefSharp; | using CefSharp; | ||||||
| using CefSharp.WinForms.Handler; | using CefSharp.WinForms.Handler; | ||||||
| using CefSharp.WinForms.Host; | using CefSharp.WinForms.Host; | ||||||
| using SafeExamBrowser.Applications.Contracts; |  | ||||||
| using SafeExamBrowser.Applications.Contracts.Events; | using SafeExamBrowser.Applications.Contracts.Events; | ||||||
| using SafeExamBrowser.Browser.Contracts.Events; | using SafeExamBrowser.Browser.Contracts.Events; | ||||||
| using SafeExamBrowser.Browser.Contracts.Filters; | using SafeExamBrowser.Browser.Contracts.Filters; | ||||||
|  | @ -43,7 +42,7 @@ using TitleChangedEventHandler = SafeExamBrowser.Applications.Contracts.Events.T | ||||||
| 
 | 
 | ||||||
| namespace SafeExamBrowser.Browser | namespace SafeExamBrowser.Browser | ||||||
| { | { | ||||||
| 	internal class BrowserWindow : IApplicationWindow | 	internal class BrowserWindow : Contracts.IBrowserWindow | ||||||
| 	{ | 	{ | ||||||
| 		private const string CLEAR_FIND_TERM = "thisisahacktoclearthesearchresultsasitappearsthatthereisnosuchfunctionalityincef"; | 		private const string CLEAR_FIND_TERM = "thisisahacktoclearthesearchresultsasitappearsthatthereisnosuchfunctionalityincef"; | ||||||
| 		private const double ZOOM_FACTOR = 0.2; | 		private const double ZOOM_FACTOR = 0.2; | ||||||
|  | @ -52,7 +51,6 @@ namespace SafeExamBrowser.Browser | ||||||
| 		private readonly IFileSystemDialog fileSystemDialog; | 		private readonly IFileSystemDialog fileSystemDialog; | ||||||
| 		private readonly IHashAlgorithm hashAlgorithm; | 		private readonly IHashAlgorithm hashAlgorithm; | ||||||
| 		private readonly HttpClient httpClient; | 		private readonly HttpClient httpClient; | ||||||
| 		private readonly bool isMainWindow; |  | ||||||
| 		private readonly IKeyGenerator keyGenerator; | 		private readonly IKeyGenerator keyGenerator; | ||||||
| 		private readonly IModuleLogger logger; | 		private readonly IModuleLogger logger; | ||||||
| 		private readonly IMessageBox messageBox; | 		private readonly IMessageBox messageBox; | ||||||
|  | @ -69,7 +67,7 @@ namespace SafeExamBrowser.Browser | ||||||
| 
 | 
 | ||||||
| 		private WindowSettings WindowSettings | 		private WindowSettings WindowSettings | ||||||
| 		{ | 		{ | ||||||
| 			get { return isMainWindow ? settings.MainWindow : settings.AdditionalWindow; } | 			get { return IsMainWindow ? settings.MainWindow : settings.AdditionalWindow; } | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		internal IBrowserControl Control { get; private set; } | 		internal IBrowserControl Control { get; private set; } | ||||||
|  | @ -77,7 +75,9 @@ namespace SafeExamBrowser.Browser | ||||||
| 
 | 
 | ||||||
| 		public IntPtr Handle { get; private set; } | 		public IntPtr Handle { get; private set; } | ||||||
| 		public IconResource Icon { get; private set; } | 		public IconResource Icon { get; private set; } | ||||||
|  | 		public bool IsMainWindow { get; private set; } | ||||||
| 		public string Title { get; private set; } | 		public string Title { get; private set; } | ||||||
|  | 		public string Url { get; private set; } | ||||||
| 
 | 
 | ||||||
| 		internal event WindowClosedEventHandler Closed; | 		internal event WindowClosedEventHandler Closed; | ||||||
| 		internal event DownloadRequestedEventHandler ConfigurationDownloadRequested; | 		internal event DownloadRequestedEventHandler ConfigurationDownloadRequested; | ||||||
|  | @ -110,7 +110,7 @@ namespace SafeExamBrowser.Browser | ||||||
| 			this.hashAlgorithm = hashAlgorithm; | 			this.hashAlgorithm = hashAlgorithm; | ||||||
| 			this.httpClient = new HttpClient(); | 			this.httpClient = new HttpClient(); | ||||||
| 			this.Id = id; | 			this.Id = id; | ||||||
| 			this.isMainWindow = isMainWindow; | 			this.IsMainWindow = isMainWindow; | ||||||
| 			this.keyGenerator = keyGenerator; | 			this.keyGenerator = keyGenerator; | ||||||
| 			this.logger = logger; | 			this.logger = logger; | ||||||
| 			this.messageBox = messageBox; | 			this.messageBox = messageBox; | ||||||
|  | @ -162,7 +162,7 @@ namespace SafeExamBrowser.Browser | ||||||
| 
 | 
 | ||||||
| 			Icon = new BrowserIconResource(); | 			Icon = new BrowserIconResource(); | ||||||
| 
 | 
 | ||||||
| 			if (isMainWindow) | 			if (IsMainWindow) | ||||||
| 			{ | 			{ | ||||||
| 				cefSharpControl = new CefSharpBrowserControl(CreateLifeSpanHandlerForMainWindow(), startUrl); | 				cefSharpControl = new CefSharpBrowserControl(CreateLifeSpanHandlerForMainWindow(), startUrl); | ||||||
| 			} | 			} | ||||||
|  | @ -203,7 +203,7 @@ namespace SafeExamBrowser.Browser | ||||||
| 
 | 
 | ||||||
| 		internal void InitializeWindow() | 		internal void InitializeWindow() | ||||||
| 		{ | 		{ | ||||||
| 			window = uiFactory.CreateBrowserWindow(Control, settings, isMainWindow, this.logger); | 			window = uiFactory.CreateBrowserWindow(Control, settings, IsMainWindow, this.logger); | ||||||
| 			window.AddressChanged += Window_AddressChanged; | 			window.AddressChanged += Window_AddressChanged; | ||||||
| 			window.BackwardNavigationRequested += Window_BackwardNavigationRequested; | 			window.BackwardNavigationRequested += Window_BackwardNavigationRequested; | ||||||
| 			window.Closed += Window_Closed; | 			window.Closed += Window_Closed; | ||||||
|  | @ -267,6 +267,8 @@ namespace SafeExamBrowser.Browser | ||||||
| 		private void Control_AddressChanged(string address) | 		private void Control_AddressChanged(string address) | ||||||
| 		{ | 		{ | ||||||
| 			logger.Info($"Navigated{(WindowSettings.UrlPolicy.CanLog() ? $" to '{address}'" : "")}."); | 			logger.Info($"Navigated{(WindowSettings.UrlPolicy.CanLog() ? $" to '{address}'" : "")}."); | ||||||
|  | 
 | ||||||
|  | 			Url = address; | ||||||
| 			window.UpdateAddress(address); | 			window.UpdateAddress(address); | ||||||
| 
 | 
 | ||||||
| 			if (WindowSettings.UrlPolicy == UrlPolicy.Always || WindowSettings.UrlPolicy == UrlPolicy.BeforeTitle) | 			if (WindowSettings.UrlPolicy == UrlPolicy.Always || WindowSettings.UrlPolicy == UrlPolicy.BeforeTitle) | ||||||
|  | @ -440,7 +442,7 @@ namespace SafeExamBrowser.Browser | ||||||
| 
 | 
 | ||||||
| 		private void HomeNavigationRequested() | 		private void HomeNavigationRequested() | ||||||
| 		{ | 		{ | ||||||
| 			if (isMainWindow && (settings.UseStartUrlAsHomeUrl || !string.IsNullOrWhiteSpace(settings.HomeUrl))) | 			if (IsMainWindow && (settings.UseStartUrlAsHomeUrl || !string.IsNullOrWhiteSpace(settings.HomeUrl))) | ||||||
| 			{ | 			{ | ||||||
| 				var navigate = false; | 				var navigate = false; | ||||||
| 				var url = settings.UseStartUrlAsHomeUrl ? settings.StartUrl : settings.HomeUrl; | 				var url = settings.UseStartUrlAsHomeUrl ? settings.StartUrl : settings.HomeUrl; | ||||||
|  |  | ||||||
|  | @ -41,6 +41,7 @@ using SafeExamBrowser.UserInterface.Contracts.Shell; | ||||||
| using SafeExamBrowser.UserInterface.Contracts.Windows; | using SafeExamBrowser.UserInterface.Contracts.Windows; | ||||||
| using SafeExamBrowser.UserInterface.Contracts.Windows.Data; | using SafeExamBrowser.UserInterface.Contracts.Windows.Data; | ||||||
| using SafeExamBrowser.WindowsApi.Contracts; | using SafeExamBrowser.WindowsApi.Contracts; | ||||||
|  | using IWindow = SafeExamBrowser.UserInterface.Contracts.Windows.IWindow; | ||||||
| 
 | 
 | ||||||
| namespace SafeExamBrowser.Client.UnitTests | namespace SafeExamBrowser.Client.UnitTests | ||||||
| { | { | ||||||
|  | @ -603,7 +604,7 @@ namespace SafeExamBrowser.Client.UnitTests | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Operations_MustAskForApplicationPath() | 		public void Operations_MustAskForApplicationPath() | ||||||
| 		{ | 		{ | ||||||
| 			var args = new ApplicationNotFoundEventArgs(default(string), default(string)); | 			var args = new ApplicationNotFoundEventArgs(default, default); | ||||||
| 			var result = new FileSystemDialogResult { FullPath = @"C:\Some\random\path\", Success = true }; | 			var result = new FileSystemDialogResult { FullPath = @"C:\Some\random\path\", Success = true }; | ||||||
| 
 | 
 | ||||||
| 			fileSystemDialog.Setup(d => d.Show( | 			fileSystemDialog.Setup(d => d.Show( | ||||||
|  | @ -627,7 +628,7 @@ namespace SafeExamBrowser.Client.UnitTests | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Operations_MustAbortAskingForApplicationPath() | 		public void Operations_MustAbortAskingForApplicationPath() | ||||||
| 		{ | 		{ | ||||||
| 			var args = new ApplicationNotFoundEventArgs(default(string), default(string)); | 			var args = new ApplicationNotFoundEventArgs(default, default); | ||||||
| 			var result = new FileSystemDialogResult { Success = false }; | 			var result = new FileSystemDialogResult { Success = false }; | ||||||
| 
 | 
 | ||||||
| 			fileSystemDialog.Setup(d => d.Show( | 			fileSystemDialog.Setup(d => d.Show( | ||||||
|  | @ -651,7 +652,7 @@ namespace SafeExamBrowser.Client.UnitTests | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Operations_MustInformAboutFailedApplicationInitialization() | 		public void Operations_MustInformAboutFailedApplicationInitialization() | ||||||
| 		{ | 		{ | ||||||
| 			var args = new ApplicationInitializationFailedEventArgs(default(string), default(string), FactoryResult.NotFound); | 			var args = new ApplicationInitializationFailedEventArgs(default, default, FactoryResult.NotFound); | ||||||
| 
 | 
 | ||||||
| 			text.SetReturnsDefault(string.Empty); | 			text.SetReturnsDefault(string.Empty); | ||||||
| 			sut.TryStart(); | 			sut.TryStart(); | ||||||
|  | @ -1172,9 +1173,9 @@ namespace SafeExamBrowser.Client.UnitTests | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Startup_MustAutoStartApplications() | 		public void Startup_MustAutoStartApplications() | ||||||
| 		{ | 		{ | ||||||
| 			var application1 = new Mock<IApplication>(); | 			var application1 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application2 = new Mock<IApplication>(); | 			var application2 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application3 = new Mock<IApplication>(); | 			var application3 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 
 | 
 | ||||||
| 			application1.SetupGet(a => a.AutoStart).Returns(true); | 			application1.SetupGet(a => a.AutoStart).Returns(true); | ||||||
| 			application2.SetupGet(a => a.AutoStart).Returns(false); | 			application2.SetupGet(a => a.AutoStart).Returns(false); | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 			var initialization = new InitializationResult(); | 			var initialization = new InitializationResult(); | ||||||
| 			var args = default(ActionRequiredEventArgs); | 			var args = default(ActionRequiredEventArgs); | ||||||
| 
 | 
 | ||||||
| 			initialization.RunningApplications.Add(new RunningApplication(default(string))); | 			initialization.RunningApplications.Add(new RunningApplication(default)); | ||||||
| 			monitor.Setup(m => m.Initialize(It.IsAny<ApplicationSettings>())).Returns(initialization); | 			monitor.Setup(m => m.Initialize(It.IsAny<ApplicationSettings>())).Returns(initialization); | ||||||
| 			sut.ActionRequired += (a) => | 			sut.ActionRequired += (a) => | ||||||
| 			{ | 			{ | ||||||
|  | @ -79,7 +79,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustAbortIfUserCancelsApplicationLocationSelection() | 		public void Perform_MustAbortIfUserCancelsApplicationLocationSelection() | ||||||
| 		{ | 		{ | ||||||
| 			var application = new Mock<IApplication>().Object; | 			var application = new Mock<IApplication<IApplicationWindow>>().Object; | ||||||
| 			var applicationSettings = new WhitelistApplication { AllowCustomPath = true }; | 			var applicationSettings = new WhitelistApplication { AllowCustomPath = true }; | ||||||
| 			var args = default(ActionRequiredEventArgs); | 			var args = default(ActionRequiredEventArgs); | ||||||
| 
 | 
 | ||||||
|  | @ -107,7 +107,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustAllowUserToChooseApplicationLocation() | 		public void Perform_MustAllowUserToChooseApplicationLocation() | ||||||
| 		{ | 		{ | ||||||
| 			var application = new Mock<IApplication>().Object; | 			var application = new Mock<IApplication<IApplicationWindow>>().Object; | ||||||
| 			var applicationSettings = new WhitelistApplication { AllowCustomPath = true }; | 			var applicationSettings = new WhitelistApplication { AllowCustomPath = true }; | ||||||
| 			var args = default(ActionRequiredEventArgs); | 			var args = default(ActionRequiredEventArgs); | ||||||
| 			var attempt = 0; | 			var attempt = 0; | ||||||
|  | @ -138,7 +138,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustDenyApplicationLocationSelection() | 		public void Perform_MustDenyApplicationLocationSelection() | ||||||
| 		{ | 		{ | ||||||
| 			var application = new Mock<IApplication>().Object; | 			var application = new Mock<IApplication<IApplicationWindow>>().Object; | ||||||
| 			var applicationSettings = new WhitelistApplication { AllowCustomPath = false }; | 			var applicationSettings = new WhitelistApplication { AllowCustomPath = false }; | ||||||
| 			var args = default(ActionRequiredEventArgs); | 			var args = default(ActionRequiredEventArgs); | ||||||
| 
 | 
 | ||||||
|  | @ -169,7 +169,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 			var initialization = new InitializationResult(); | 			var initialization = new InitializationResult(); | ||||||
| 			var args = default(ActionRequiredEventArgs); | 			var args = default(ActionRequiredEventArgs); | ||||||
| 
 | 
 | ||||||
| 			initialization.FailedAutoTerminations.Add(new RunningApplication(default(string))); | 			initialization.FailedAutoTerminations.Add(new RunningApplication(default)); | ||||||
| 			monitor.Setup(m => m.Initialize(It.IsAny<ApplicationSettings>())).Returns(initialization); | 			monitor.Setup(m => m.Initialize(It.IsAny<ApplicationSettings>())).Returns(initialization); | ||||||
| 			sut.ActionRequired += (a) => args = a; | 			sut.ActionRequired += (a) => args = a; | ||||||
| 
 | 
 | ||||||
|  | @ -185,7 +185,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustFailIfTerminationFails() | 		public void Perform_MustFailIfTerminationFails() | ||||||
| 		{ | 		{ | ||||||
| 			var application = new RunningApplication(default(string)); | 			var application = new RunningApplication(default); | ||||||
| 			var initialization = new InitializationResult(); | 			var initialization = new InitializationResult(); | ||||||
| 			var args = new List<ActionRequiredEventArgs>(); | 			var args = new List<ActionRequiredEventArgs>(); | ||||||
| 
 | 
 | ||||||
|  | @ -215,7 +215,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustIndicateApplicationInitializationFailure() | 		public void Perform_MustIndicateApplicationInitializationFailure() | ||||||
| 		{ | 		{ | ||||||
| 			var application = new Mock<IApplication>().Object; | 			var application = new Mock<IApplication<IApplicationWindow>>().Object; | ||||||
| 			var applicationSettings = new WhitelistApplication(); | 			var applicationSettings = new WhitelistApplication(); | ||||||
| 			var args = default(ActionRequiredEventArgs); | 			var args = default(ActionRequiredEventArgs); | ||||||
| 
 | 
 | ||||||
|  | @ -235,9 +235,9 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustInitializeApplications() | 		public void Perform_MustInitializeApplications() | ||||||
| 		{ | 		{ | ||||||
| 			var application1 = new Mock<IApplication>().Object; | 			var application1 = new Mock<IApplication<IApplicationWindow>>().Object; | ||||||
| 			var application2 = new Mock<IApplication>().Object; | 			var application2 = new Mock<IApplication<IApplicationWindow>>().Object; | ||||||
| 			var application3 = new Mock<IApplication>().Object; | 			var application3 = new Mock<IApplication<IApplicationWindow>>().Object; | ||||||
| 			var application1Settings = new WhitelistApplication(); | 			var application1Settings = new WhitelistApplication(); | ||||||
| 			var application2Settings = new WhitelistApplication(); | 			var application2Settings = new WhitelistApplication(); | ||||||
| 			var application3Settings = new WhitelistApplication(); | 			var application3Settings = new WhitelistApplication(); | ||||||
|  | @ -297,9 +297,9 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustTerminateRunningApplications() | 		public void Perform_MustTerminateRunningApplications() | ||||||
| 		{ | 		{ | ||||||
| 			var application1 = new RunningApplication(default(string)); | 			var application1 = new RunningApplication(default); | ||||||
| 			var application2 = new RunningApplication(default(string)); | 			var application2 = new RunningApplication(default); | ||||||
| 			var application3 = new RunningApplication(default(string)); | 			var application3 = new RunningApplication(default); | ||||||
| 			var initialization = new InitializationResult(); | 			var initialization = new InitializationResult(); | ||||||
| 			var args = default(ActionRequiredEventArgs); | 			var args = default(ActionRequiredEventArgs); | ||||||
| 
 | 
 | ||||||
|  | @ -362,9 +362,9 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Revert_MustTerminateApplications() | 		public void Revert_MustTerminateApplications() | ||||||
| 		{ | 		{ | ||||||
| 			var application1 = new Mock<IApplication>(); | 			var application1 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application2 = new Mock<IApplication>(); | 			var application2 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application3 = new Mock<IApplication>(); | 			var application3 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 
 | 
 | ||||||
| 			context.Applications.Add(application1.Object); | 			context.Applications.Add(application1.Object); | ||||||
| 			context.Applications.Add(application2.Object); | 			context.Applications.Add(application2.Object); | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 			sut.Perform(); | 			sut.Perform(); | ||||||
| 
 | 
 | ||||||
| 			browser.Verify(c => c.Initialize(), Times.Once); | 			browser.Verify(c => c.Initialize(), Times.Once); | ||||||
| 			taskview.Verify(t => t.Add(It.Is<IApplication>(a => a == context.Browser)), Times.Once); | 			taskview.Verify(t => t.Add(It.Is<IApplication<IApplicationWindow>>(a => a == context.Browser)), Times.Once); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
|  | @ -73,7 +73,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 			actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>(), true), Times.Never); | 			actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>(), true), Times.Never); | ||||||
| 			browser.Verify(c => c.Initialize(), Times.Never); | 			browser.Verify(c => c.Initialize(), Times.Never); | ||||||
| 			taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>(), true), Times.Never); | 			taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>(), true), Times.Never); | ||||||
| 			taskview.Verify(t => t.Add(It.Is<IApplication>(a => a == context.Browser)), Times.Never); | 			taskview.Verify(t => t.Add(It.Is<IApplication<IApplicationWindow>>(a => a == context.Browser)), Times.Never); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
|  |  | ||||||
|  | @ -116,11 +116,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustInitializeApplications() | 		public void Perform_MustInitializeApplications() | ||||||
| 		{ | 		{ | ||||||
| 			var application1 = new Mock<IApplication>(); | 			var application1 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application1Settings = new WhitelistApplication { ShowInShell = true }; | 			var application1Settings = new WhitelistApplication { ShowInShell = true }; | ||||||
| 			var application2 = new Mock<IApplication>(); | 			var application2 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application2Settings = new WhitelistApplication { ShowInShell = false }; | 			var application2Settings = new WhitelistApplication { ShowInShell = false }; | ||||||
| 			var application3 = new Mock<IApplication>(); | 			var application3 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application3Settings = new WhitelistApplication { ShowInShell = true }; | 			var application3Settings = new WhitelistApplication { ShowInShell = true }; | ||||||
| 
 | 
 | ||||||
| 			application1.SetupGet(a => a.Id).Returns(application1Settings.Id); | 			application1.SetupGet(a => a.Id).Returns(application1Settings.Id); | ||||||
|  | @ -140,25 +140,25 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>(), false), Times.Exactly(2)); | 			actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>(), false), Times.Exactly(2)); | ||||||
| 			taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>(), false), Times.Exactly(2)); | 			taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>(), false), Times.Exactly(2)); | ||||||
| 			taskview.Verify(t => t.Add(It.Is<IApplication>(a => a == application1.Object)), Times.Once); | 			taskview.Verify(t => t.Add(It.Is<IApplication<IApplicationWindow>>(a => a == application1.Object)), Times.Once); | ||||||
| 			taskview.Verify(t => t.Add(It.Is<IApplication>(a => a == application2.Object)), Times.Once); | 			taskview.Verify(t => t.Add(It.Is<IApplication<IApplicationWindow>>(a => a == application2.Object)), Times.Once); | ||||||
| 			taskview.Verify(t => t.Add(It.Is<IApplication>(a => a == application3.Object)), Times.Once); | 			taskview.Verify(t => t.Add(It.Is<IApplication<IApplicationWindow>>(a => a == application3.Object)), Times.Once); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application1.Object), Location.ActionCenter), Times.Once); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application1.Object), Location.ActionCenter), Times.Once); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application1.Object), Location.Taskbar), Times.Once); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application1.Object), Location.Taskbar), Times.Once); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application2.Object), Location.ActionCenter), Times.Never); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application2.Object), Location.ActionCenter), Times.Never); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application2.Object), Location.Taskbar), Times.Never); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application2.Object), Location.Taskbar), Times.Never); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application3.Object), Location.ActionCenter), Times.Once); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application3.Object), Location.ActionCenter), Times.Once); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application3.Object), Location.Taskbar), Times.Once); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application3.Object), Location.Taskbar), Times.Once); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
| 		public void Perform_MustNotAddApplicationsToShellIfNotEnabled() | 		public void Perform_MustNotAddApplicationsToShellIfNotEnabled() | ||||||
| 		{ | 		{ | ||||||
| 			var application1 = new Mock<IApplication>(); | 			var application1 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application1Settings = new WhitelistApplication { ShowInShell = true }; | 			var application1Settings = new WhitelistApplication { ShowInShell = true }; | ||||||
| 			var application2 = new Mock<IApplication>(); | 			var application2 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application2Settings = new WhitelistApplication { ShowInShell = true }; | 			var application2Settings = new WhitelistApplication { ShowInShell = true }; | ||||||
| 			var application3 = new Mock<IApplication>(); | 			var application3 = new Mock<IApplication<IApplicationWindow>>(); | ||||||
| 			var application3Settings = new WhitelistApplication { ShowInShell = true }; | 			var application3Settings = new WhitelistApplication { ShowInShell = true }; | ||||||
| 
 | 
 | ||||||
| 			application1.SetupGet(a => a.Id).Returns(application1Settings.Id); | 			application1.SetupGet(a => a.Id).Returns(application1Settings.Id); | ||||||
|  | @ -178,15 +178,15 @@ namespace SafeExamBrowser.Client.UnitTests.Operations | ||||||
| 
 | 
 | ||||||
| 			actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>(), false), Times.Never); | 			actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>(), false), Times.Never); | ||||||
| 			taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>(), false), Times.Never); | 			taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>(), false), Times.Never); | ||||||
| 			taskview.Verify(t => t.Add(It.Is<IApplication>(a => a == application1.Object)), Times.Once); | 			taskview.Verify(t => t.Add(It.Is<IApplication<IApplicationWindow>>(a => a == application1.Object)), Times.Once); | ||||||
| 			taskview.Verify(t => t.Add(It.Is<IApplication>(a => a == application2.Object)), Times.Once); | 			taskview.Verify(t => t.Add(It.Is<IApplication<IApplicationWindow>>(a => a == application2.Object)), Times.Once); | ||||||
| 			taskview.Verify(t => t.Add(It.Is<IApplication>(a => a == application3.Object)), Times.Once); | 			taskview.Verify(t => t.Add(It.Is<IApplication<IApplicationWindow>>(a => a == application3.Object)), Times.Once); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application1.Object), Location.ActionCenter), Times.Never); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application1.Object), Location.ActionCenter), Times.Never); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application1.Object), Location.Taskbar), Times.Never); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application1.Object), Location.Taskbar), Times.Never); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application2.Object), Location.ActionCenter), Times.Never); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application2.Object), Location.ActionCenter), Times.Never); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application2.Object), Location.Taskbar), Times.Never); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application2.Object), Location.Taskbar), Times.Never); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application3.Object), Location.ActionCenter), Times.Never); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application3.Object), Location.ActionCenter), Times.Never); | ||||||
| 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication>(a => a == application3.Object), Location.Taskbar), Times.Never); | 			uiFactory.Verify(f => f.CreateApplicationControl(It.Is<IApplication<IApplicationWindow>>(a => a == application3.Object), Location.Taskbar), Times.Never); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		[TestMethod] | 		[TestMethod] | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ namespace SafeExamBrowser.Client | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// All applications allowed for the current session. | 		/// All applications allowed for the current session. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		internal IList<IApplication> Applications { get; } | 		internal IList<IApplication<IApplicationWindow>> Applications { get; } | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// The global application configuration. | 		/// The global application configuration. | ||||||
|  | @ -72,7 +72,7 @@ namespace SafeExamBrowser.Client | ||||||
| 		internal ClientContext() | 		internal ClientContext() | ||||||
| 		{ | 		{ | ||||||
| 			Activators = new List<IActivator>(); | 			Activators = new List<IActivator>(); | ||||||
| 			Applications = new List<IApplication>(); | 			Applications = new List<IApplication<IApplicationWindow>>(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -73,6 +73,7 @@ namespace SafeExamBrowser.Client | ||||||
| 		private UserInterfaceMode uiMode; | 		private UserInterfaceMode uiMode; | ||||||
| 
 | 
 | ||||||
| 		private IActionCenter actionCenter; | 		private IActionCenter actionCenter; | ||||||
|  | 		private ApplicationMonitor applicationMonitor; | ||||||
| 		private ILogger logger; | 		private ILogger logger; | ||||||
| 		private IMessageBox messageBox; | 		private IMessageBox messageBox; | ||||||
| 		private INativeMethods nativeMethods; | 		private INativeMethods nativeMethods; | ||||||
|  | @ -95,6 +96,7 @@ namespace SafeExamBrowser.Client | ||||||
| 			InitializeLogging(); | 			InitializeLogging(); | ||||||
| 			InitializeText(); | 			InitializeText(); | ||||||
| 
 | 
 | ||||||
|  | 			var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory))); | ||||||
| 			var registry = new Registry(ModuleLogger(nameof(Registry))); | 			var registry = new Registry(ModuleLogger(nameof(Registry))); | ||||||
| 
 | 
 | ||||||
| 			uiFactory = BuildUserInterfaceFactory(); | 			uiFactory = BuildUserInterfaceFactory(); | ||||||
|  | @ -102,6 +104,7 @@ namespace SafeExamBrowser.Client | ||||||
| 			context = new ClientContext(); | 			context = new ClientContext(); | ||||||
| 			messageBox = BuildMessageBox(); | 			messageBox = BuildMessageBox(); | ||||||
| 			nativeMethods = new NativeMethods(); | 			nativeMethods = new NativeMethods(); | ||||||
|  | 			applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, processFactory); | ||||||
| 			networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods); | 			networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods); | ||||||
| 			runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client); | 			runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client); | ||||||
| 			systemInfo = new SystemInfo(registry); | 			systemInfo = new SystemInfo(registry); | ||||||
|  | @ -109,8 +112,6 @@ namespace SafeExamBrowser.Client | ||||||
| 			taskview = uiFactory.CreateTaskview(); | 			taskview = uiFactory.CreateTaskview(); | ||||||
| 			userInfo = new UserInfo(ModuleLogger(nameof(UserInfo))); | 			userInfo = new UserInfo(ModuleLogger(nameof(UserInfo))); | ||||||
| 
 | 
 | ||||||
| 			var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory))); |  | ||||||
| 			var applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, processFactory); |  | ||||||
| 			var applicationFactory = new ApplicationFactory(applicationMonitor, ModuleLogger(nameof(ApplicationFactory)), nativeMethods, processFactory, new Registry(ModuleLogger(nameof(Registry)))); | 			var applicationFactory = new ApplicationFactory(applicationMonitor, ModuleLogger(nameof(ApplicationFactory)), nativeMethods, processFactory, new Registry(ModuleLogger(nameof(Registry)))); | ||||||
| 			var clipboard = new Clipboard(ModuleLogger(nameof(Clipboard)), nativeMethods); | 			var clipboard = new Clipboard(ModuleLogger(nameof(Clipboard)), nativeMethods); | ||||||
| 			var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo); | 			var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo); | ||||||
|  | @ -288,7 +289,16 @@ namespace SafeExamBrowser.Client | ||||||
| 
 | 
 | ||||||
| 		private IOperation BuildProctoringOperation() | 		private IOperation BuildProctoringOperation() | ||||||
| 		{ | 		{ | ||||||
| 			var controller = new ProctoringController(context.AppConfig, new FileSystem(), ModuleLogger(nameof(ProctoringController)), nativeMethods, context.Server, text, uiFactory); | 			var controller = new ProctoringController( | ||||||
|  | 				context.AppConfig, | ||||||
|  | 				applicationMonitor, | ||||||
|  | 				context.Browser, | ||||||
|  | 				new FileSystem(), | ||||||
|  | 				ModuleLogger(nameof(ProctoringController)), | ||||||
|  | 				nativeMethods, | ||||||
|  | 				context.Server, | ||||||
|  | 				text, | ||||||
|  | 				uiFactory); | ||||||
| 			var operation = new ProctoringOperation(actionCenter, context, controller, logger, taskbar, uiFactory); | 			var operation = new ProctoringOperation(actionCenter, context, controller, logger, taskbar, uiFactory); | ||||||
| 
 | 
 | ||||||
| 			return operation; | 			return operation; | ||||||
|  |  | ||||||
|  | @ -17,11 +17,11 @@ namespace SafeExamBrowser.Client.Operations | ||||||
| { | { | ||||||
| 	internal class BrowserOperation : ClientOperation | 	internal class BrowserOperation : ClientOperation | ||||||
| 	{ | 	{ | ||||||
| 		private IActionCenter actionCenter; | 		private readonly IActionCenter actionCenter; | ||||||
| 		private ILogger logger; | 		private readonly ILogger logger; | ||||||
| 		private ITaskbar taskbar; | 		private readonly ITaskbar taskbar; | ||||||
| 		private ITaskview taskview; | 		private readonly ITaskview taskview; | ||||||
| 		private IUserInterfaceFactory uiFactory; | 		private readonly IUserInterfaceFactory uiFactory; | ||||||
| 
 | 
 | ||||||
| 		public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } | 		public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } | ||||||
| 		public override event StatusChangedEventHandler StatusChanged; | 		public override event StatusChangedEventHandler StatusChanged; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,34 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2023 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.WindowsApi.Contracts; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Monitoring.Contracts.Applications | ||||||
|  | { | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Provides information about the currently active application. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public class ActiveApplication | ||||||
|  | 	{ | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// The process which owns the currently active window. | ||||||
|  | 		/// </summary> | ||||||
|  | 		public IProcess Process { get; } | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// The currently active window (i.e. the window which currently has input focus). | ||||||
|  | 		/// </summary> | ||||||
|  | 		public IWindow Window { get; } | ||||||
|  | 
 | ||||||
|  | 		public ActiveApplication(IProcess process, IWindow window) | ||||||
|  | 		{ | ||||||
|  | 			Process = process; | ||||||
|  | 			Window = window; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -46,6 +46,12 @@ namespace SafeExamBrowser.Monitoring.Contracts.Applications | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		void Stop(); | 		void Stop(); | ||||||
| 
 | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// Attempts to retrieve the currently active application (i.e. the application which currently has input focus). Returns <c>true</c> if | ||||||
|  | 		/// successful, otherwise <c>false</c>. | ||||||
|  | 		/// </summary> | ||||||
|  | 		bool TryGetActiveApplication(out ActiveApplication application); | ||||||
|  | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Attempts to terminate all processes of the specified application. Returns <c>true</c> if successful, otherwise <c>false</c>. | 		/// Attempts to terminate all processes of the specified application. Returns <c>true</c> if successful, otherwise <c>false</c>. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								SafeExamBrowser.Monitoring.Contracts/Applications/IWindow.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								SafeExamBrowser.Monitoring.Contracts/Applications/IWindow.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2023 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; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Monitoring.Contracts.Applications | ||||||
|  | { | ||||||
|  | 	/// <summary> | ||||||
|  | 	/// Represents a native window handled by the operating system. | ||||||
|  | 	/// </summary> | ||||||
|  | 	public interface IWindow | ||||||
|  | 	{ | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// The handle of the window. | ||||||
|  | 		/// </summary> | ||||||
|  | 		IntPtr Handle { get; } | ||||||
|  | 
 | ||||||
|  | 		/// <summary> | ||||||
|  | 		/// The title of the window. | ||||||
|  | 		/// </summary> | ||||||
|  | 		string Title { get; } | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -55,8 +55,10 @@ | ||||||
|     <Reference Include="Microsoft.CSharp" /> |     <Reference Include="Microsoft.CSharp" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|  |     <Compile Include="Applications\ActiveApplication.cs" /> | ||||||
|     <Compile Include="Applications\Events\InstanceStartedEventHandler.cs" /> |     <Compile Include="Applications\Events\InstanceStartedEventHandler.cs" /> | ||||||
|     <Compile Include="Applications\Events\TerminationFailedEventHandler.cs" /> |     <Compile Include="Applications\Events\TerminationFailedEventHandler.cs" /> | ||||||
|  |     <Compile Include="Applications\IWindow.cs" /> | ||||||
|     <Compile Include="Applications\RunningApplication.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" /> | ||||||
|  |  | ||||||
|  | @ -92,6 +92,18 @@ namespace SafeExamBrowser.Monitoring.Applications | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		public bool TryGetActiveApplication(out ActiveApplication application) | ||||||
|  | 		{ | ||||||
|  | 			application = default; | ||||||
|  | 
 | ||||||
|  | 			if (activeWindow != default && TryGetProcessFor(activeWindow, out var process)) | ||||||
|  | 			{ | ||||||
|  | 				application = new ActiveApplication(process, new Window { Handle = activeWindow.Handle, Title = activeWindow.Title }); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return application != default; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		public bool TryTerminate(RunningApplication application) | 		public bool TryTerminate(RunningApplication application) | ||||||
| 		{ | 		{ | ||||||
| 			var success = true; | 			var success = true; | ||||||
|  | @ -355,23 +367,19 @@ namespace SafeExamBrowser.Monitoring.Applications | ||||||
| 
 | 
 | ||||||
| 		private bool IsAllowed(Window window) | 		private bool IsAllowed(Window window) | ||||||
| 		{ | 		{ | ||||||
| 			var processId = Convert.ToInt32(nativeMethods.GetProcessIdFor(window.Handle)); | 			var allowed = false; | ||||||
| 
 | 
 | ||||||
| 			if (processFactory.TryGetById(processId, out var process)) | 			if (TryGetProcessFor(window, out var process)) | ||||||
| 			{ | 			{ | ||||||
| 				if (BelongsToSafeExamBrowser(process) || IsWhitelisted(process, out _)) | 				allowed = BelongsToSafeExamBrowser(process) || IsWhitelisted(process, out _); | ||||||
| 				{ |  | ||||||
| 					return true; |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 				logger.Warn($"Window {window} belongs to not whitelisted process '{process.Name}'!"); | 			if (!allowed) | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 			{ | 			{ | ||||||
| 				logger.Error($"Could not find process for window {window} and process ID = {processId}!"); | 				logger.Warn($"Window {window} belongs to not whitelisted process '{process?.Name ?? "n/a"}'!"); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			return false; | 			return allowed; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private bool IsWhitelisted(IProcess process, out Guid? applicationId) | 		private bool IsWhitelisted(IProcess process, out Guid? applicationId) | ||||||
|  | @ -391,6 +399,18 @@ namespace SafeExamBrowser.Monitoring.Applications | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		private bool TryGetProcessFor(Window window, out IProcess process) | ||||||
|  | 		{ | ||||||
|  | 			var processId = Convert.ToInt32(nativeMethods.GetProcessIdFor(window.Handle)); | ||||||
|  | 
 | ||||||
|  | 			if (!processFactory.TryGetById(processId, out process)) | ||||||
|  | 			{ | ||||||
|  | 				logger.Error($"Could not find process for window {window} and process ID = {processId}!"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return process != default; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		private bool TryHide(Window window) | 		private bool TryHide(Window window) | ||||||
| 		{ | 		{ | ||||||
| 			var success = nativeMethods.HideWindow(window.Handle); | 			var success = nativeMethods.HideWindow(window.Handle); | ||||||
|  |  | ||||||
|  | @ -7,10 +7,11 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| using System; | using System; | ||||||
|  | using SafeExamBrowser.Monitoring.Contracts.Applications; | ||||||
| 
 | 
 | ||||||
| namespace SafeExamBrowser.Monitoring.Applications | namespace SafeExamBrowser.Monitoring.Applications | ||||||
| { | { | ||||||
| 	internal class Window | 	internal class Window : IWindow | ||||||
| 	{ | 	{ | ||||||
| 		public IntPtr Handle { get; set; } | 		public IntPtr Handle { get; set; } | ||||||
| 		public string Title { get; set; } | 		public string Title { get; set; } | ||||||
|  |  | ||||||
|  | @ -8,10 +8,12 @@ | ||||||
| 
 | 
 | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
|  | using SafeExamBrowser.Browser.Contracts; | ||||||
| using SafeExamBrowser.Configuration.Contracts; | using SafeExamBrowser.Configuration.Contracts; | ||||||
| using SafeExamBrowser.Core.Contracts.Notifications; | using SafeExamBrowser.Core.Contracts.Notifications; | ||||||
| using SafeExamBrowser.I18n.Contracts; | using SafeExamBrowser.I18n.Contracts; | ||||||
| using SafeExamBrowser.Logging.Contracts; | using SafeExamBrowser.Logging.Contracts; | ||||||
|  | using SafeExamBrowser.Monitoring.Contracts.Applications; | ||||||
| using SafeExamBrowser.Proctoring.Contracts; | using SafeExamBrowser.Proctoring.Contracts; | ||||||
| using SafeExamBrowser.Proctoring.Contracts.Events; | using SafeExamBrowser.Proctoring.Contracts.Events; | ||||||
| using SafeExamBrowser.Server.Contracts; | using SafeExamBrowser.Server.Contracts; | ||||||
|  | @ -39,6 +41,8 @@ namespace SafeExamBrowser.Proctoring | ||||||
| 
 | 
 | ||||||
| 		public ProctoringController( | 		public ProctoringController( | ||||||
| 			AppConfig appConfig, | 			AppConfig appConfig, | ||||||
|  | 			IApplicationMonitor applicationMonitor, | ||||||
|  | 			IBrowserApplication browser, | ||||||
| 			IFileSystem fileSystem, | 			IFileSystem fileSystem, | ||||||
| 			IModuleLogger logger, | 			IModuleLogger logger, | ||||||
| 			INativeMethods nativeMethods, | 			INativeMethods nativeMethods, | ||||||
|  | @ -49,7 +53,7 @@ namespace SafeExamBrowser.Proctoring | ||||||
| 			this.logger = logger; | 			this.logger = logger; | ||||||
| 			this.server = server; | 			this.server = server; | ||||||
| 
 | 
 | ||||||
| 			factory = new ProctoringFactory(appConfig, fileSystem, logger, nativeMethods, text, uiFactory); | 			factory = new ProctoringFactory(appConfig, applicationMonitor, browser, fileSystem, logger, nativeMethods, text, uiFactory); | ||||||
| 			implementations = new List<ProctoringImplementation>(); | 			implementations = new List<ProctoringImplementation>(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,9 +7,11 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
|  | using SafeExamBrowser.Browser.Contracts; | ||||||
| using SafeExamBrowser.Configuration.Contracts; | using SafeExamBrowser.Configuration.Contracts; | ||||||
| using SafeExamBrowser.I18n.Contracts; | using SafeExamBrowser.I18n.Contracts; | ||||||
| using SafeExamBrowser.Logging.Contracts; | using SafeExamBrowser.Logging.Contracts; | ||||||
|  | using SafeExamBrowser.Monitoring.Contracts.Applications; | ||||||
| using SafeExamBrowser.Proctoring.JitsiMeet; | using SafeExamBrowser.Proctoring.JitsiMeet; | ||||||
| using SafeExamBrowser.Proctoring.ScreenProctoring; | using SafeExamBrowser.Proctoring.ScreenProctoring; | ||||||
| using SafeExamBrowser.Proctoring.ScreenProctoring.Service; | using SafeExamBrowser.Proctoring.ScreenProctoring.Service; | ||||||
|  | @ -23,6 +25,8 @@ namespace SafeExamBrowser.Proctoring | ||||||
| 	internal class ProctoringFactory | 	internal class ProctoringFactory | ||||||
| 	{ | 	{ | ||||||
| 		private readonly AppConfig appConfig; | 		private readonly AppConfig appConfig; | ||||||
|  | 		private readonly IApplicationMonitor applicationMonitor; | ||||||
|  | 		private readonly IBrowserApplication browser; | ||||||
| 		private readonly IFileSystem fileSystem; | 		private readonly IFileSystem fileSystem; | ||||||
| 		private readonly IModuleLogger logger; | 		private readonly IModuleLogger logger; | ||||||
| 		private readonly INativeMethods nativeMethods; | 		private readonly INativeMethods nativeMethods; | ||||||
|  | @ -31,6 +35,8 @@ namespace SafeExamBrowser.Proctoring | ||||||
| 
 | 
 | ||||||
| 		public ProctoringFactory( | 		public ProctoringFactory( | ||||||
| 			AppConfig appConfig, | 			AppConfig appConfig, | ||||||
|  | 			IApplicationMonitor applicationMonitor, | ||||||
|  | 			IBrowserApplication browser, | ||||||
| 			IFileSystem fileSystem, | 			IFileSystem fileSystem, | ||||||
| 			IModuleLogger logger, | 			IModuleLogger logger, | ||||||
| 			INativeMethods nativeMethods, | 			INativeMethods nativeMethods, | ||||||
|  | @ -38,6 +44,8 @@ namespace SafeExamBrowser.Proctoring | ||||||
| 			IUserInterfaceFactory uiFactory) | 			IUserInterfaceFactory uiFactory) | ||||||
| 		{ | 		{ | ||||||
| 			this.appConfig = appConfig; | 			this.appConfig = appConfig; | ||||||
|  | 			this.applicationMonitor = applicationMonitor; | ||||||
|  | 			this.browser = browser; | ||||||
| 			this.fileSystem = fileSystem; | 			this.fileSystem = fileSystem; | ||||||
| 			this.logger = logger; | 			this.logger = logger; | ||||||
| 			this.nativeMethods = nativeMethods; | 			this.nativeMethods = nativeMethods; | ||||||
|  | @ -61,7 +69,7 @@ namespace SafeExamBrowser.Proctoring | ||||||
| 				var logger = this.logger.CloneFor(nameof(ScreenProctoring)); | 				var logger = this.logger.CloneFor(nameof(ScreenProctoring)); | ||||||
| 				var service = new ServiceProxy(logger.CloneFor(nameof(ServiceProxy))); | 				var service = new ServiceProxy(logger.CloneFor(nameof(ServiceProxy))); | ||||||
| 
 | 
 | ||||||
| 				implementations.Add(new ScreenProctoringImplementation(logger, nativeMethods, service, settings, text)); | 				implementations.Add(new ScreenProctoringImplementation(applicationMonitor, browser, logger, nativeMethods, service, settings, text)); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			return implementations; | 			return implementations; | ||||||
|  |  | ||||||
|  | @ -91,7 +91,11 @@ | ||||||
|     <Compile Include="ProctoringFactory.cs" /> |     <Compile Include="ProctoringFactory.cs" /> | ||||||
|     <Compile Include="ProctoringImplementation.cs" /> |     <Compile Include="ProctoringImplementation.cs" /> | ||||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> |     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||||
|  |     <Compile Include="ScreenProctoring\Data\IntervalTrigger.cs" /> | ||||||
|  |     <Compile Include="ScreenProctoring\Data\KeyboardTrigger.cs" /> | ||||||
|  |     <Compile Include="ScreenProctoring\Data\MouseTrigger.cs" /> | ||||||
|     <Compile Include="ScreenProctoring\Imaging\Extensions.cs" /> |     <Compile Include="ScreenProctoring\Imaging\Extensions.cs" /> | ||||||
|  |     <Compile Include="ScreenProctoring\Data\Metadata.cs" /> | ||||||
|     <Compile Include="ScreenProctoring\Imaging\ProcessingOrder.cs" /> |     <Compile Include="ScreenProctoring\Imaging\ProcessingOrder.cs" /> | ||||||
|     <Compile Include="ScreenProctoring\Imaging\ScreenShot.cs" /> |     <Compile Include="ScreenProctoring\Imaging\ScreenShot.cs" /> | ||||||
|     <Compile Include="ScreenProctoring\ScreenProctoringImplementation.cs" /> |     <Compile Include="ScreenProctoring\ScreenProctoringImplementation.cs" /> | ||||||
|  | @ -109,6 +113,14 @@ | ||||||
|     <Compile Include="ScreenProctoring\Service\ServiceResponse.cs" /> |     <Compile Include="ScreenProctoring\Service\ServiceResponse.cs" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <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.Browser.Contracts\SafeExamBrowser.Browser.Contracts.csproj"> | ||||||
|  |       <Project>{5FB5273D-277C-41DD-8593-A25CE1AFF2E9}</Project> | ||||||
|  |       <Name>SafeExamBrowser.Browser.Contracts</Name> | ||||||
|  |     </ProjectReference> | ||||||
|     <ProjectReference Include="..\SafeExamBrowser.Configuration.Contracts\SafeExamBrowser.Configuration.Contracts.csproj"> |     <ProjectReference Include="..\SafeExamBrowser.Configuration.Contracts\SafeExamBrowser.Configuration.Contracts.csproj"> | ||||||
|       <Project>{7d74555e-63e1-4c46-bd0a-8580552368c8}</Project> |       <Project>{7d74555e-63e1-4c46-bd0a-8580552368c8}</Project> | ||||||
|       <Name>SafeExamBrowser.Configuration.Contracts</Name> |       <Name>SafeExamBrowser.Configuration.Contracts</Name> | ||||||
|  | @ -125,6 +137,10 @@ | ||||||
|       <Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project> |       <Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project> | ||||||
|       <Name>SafeExamBrowser.Logging.Contracts</Name> |       <Name>SafeExamBrowser.Logging.Contracts</Name> | ||||||
|     </ProjectReference> |     </ProjectReference> | ||||||
|  |     <ProjectReference Include="..\SafeExamBrowser.Monitoring.Contracts\SafeExamBrowser.Monitoring.Contracts.csproj"> | ||||||
|  |       <Project>{6D563A30-366D-4C35-815B-2C9E6872278B}</Project> | ||||||
|  |       <Name>SafeExamBrowser.Monitoring.Contracts</Name> | ||||||
|  |     </ProjectReference> | ||||||
|     <ProjectReference Include="..\SafeExamBrowser.Proctoring.Contracts\SafeExamBrowser.Proctoring.Contracts.csproj"> |     <ProjectReference Include="..\SafeExamBrowser.Proctoring.Contracts\SafeExamBrowser.Proctoring.Contracts.csproj"> | ||||||
|       <Project>{8e52bd1c-0540-4f16-b181-6665d43f7a7b}</Project> |       <Project>{8e52bd1c-0540-4f16-b181-6665d43f7a7b}</Project> | ||||||
|       <Name>SafeExamBrowser.Proctoring.Contracts</Name> |       <Name>SafeExamBrowser.Proctoring.Contracts</Name> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2023 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.Proctoring.ScreenProctoring.Data | ||||||
|  | { | ||||||
|  | 	internal class IntervalTrigger | ||||||
|  | 	{ | ||||||
|  | 		public int ConfigurationValue { get; internal set; } | ||||||
|  | 		public int TimeElapsed { get; internal set; } | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,20 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2023 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.Windows.Input; | ||||||
|  | using SafeExamBrowser.WindowsApi.Contracts.Events; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Proctoring.ScreenProctoring.Data | ||||||
|  | { | ||||||
|  | 	internal class KeyboardTrigger | ||||||
|  | 	{ | ||||||
|  | 		public Key Key { get; internal set; } | ||||||
|  | 		public KeyModifier Modifier { get; internal set; } | ||||||
|  | 		public KeyState State { get; internal set; } | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										158
									
								
								SafeExamBrowser.Proctoring/ScreenProctoring/Data/Metadata.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								SafeExamBrowser.Proctoring/ScreenProctoring/Data/Metadata.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,158 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2023 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.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using Newtonsoft.Json; | ||||||
|  | using Newtonsoft.Json.Linq; | ||||||
|  | using SafeExamBrowser.Browser.Contracts; | ||||||
|  | using SafeExamBrowser.Logging.Contracts; | ||||||
|  | using SafeExamBrowser.Monitoring.Contracts.Applications; | ||||||
|  | using SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests; | ||||||
|  | using SafeExamBrowser.WindowsApi.Contracts.Events; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Proctoring.ScreenProctoring.Data | ||||||
|  | { | ||||||
|  | 	internal class Metadata | ||||||
|  | 	{ | ||||||
|  | 		private readonly IApplicationMonitor applicationMonitor; | ||||||
|  | 		private readonly IBrowserApplication browser; | ||||||
|  | 		private readonly ILogger logger; | ||||||
|  | 
 | ||||||
|  | 		internal string ApplicationInfo { get; private set; } | ||||||
|  | 		internal string BrowserInfo { get; private set; } | ||||||
|  | 		internal string TriggerInfo { get; private set; } | ||||||
|  | 		internal string Urls { get; private set; } | ||||||
|  | 		internal string WindowTitle { get; private set; } | ||||||
|  | 
 | ||||||
|  | 		internal Metadata(IApplicationMonitor applicationMonitor, IBrowserApplication browser, ILogger logger) | ||||||
|  | 		{ | ||||||
|  | 			this.applicationMonitor = applicationMonitor; | ||||||
|  | 			this.browser = browser; | ||||||
|  | 			this.logger = logger; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		internal void Capture(IntervalTrigger interval = default, KeyboardTrigger keyboard = default, MouseTrigger mouse = default) | ||||||
|  | 		{ | ||||||
|  | 			CaptureApplicationData(); | ||||||
|  | 			CaptureBrowserData(); | ||||||
|  | 
 | ||||||
|  | 			if (interval != default) | ||||||
|  | 			{ | ||||||
|  | 				CaptureIntervalTrigger(interval); | ||||||
|  | 			} | ||||||
|  | 			else if (keyboard != default) | ||||||
|  | 			{ | ||||||
|  | 				CaptureKeyboardTrigger(keyboard); | ||||||
|  | 			} | ||||||
|  | 			else if (mouse != default) | ||||||
|  | 			{ | ||||||
|  | 				CaptureMouseTrigger(mouse); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			logger.Debug($"Captured metadata: {ApplicationInfo} / {BrowserInfo} / {TriggerInfo} / {Urls} / {WindowTitle}."); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		internal string ToJson() | ||||||
|  | 		{ | ||||||
|  | 			var json = new JObject | ||||||
|  | 			{ | ||||||
|  | 				[Header.Metadata.ApplicationInfo] = ApplicationInfo, | ||||||
|  | 				[Header.Metadata.BrowserInfo] = BrowserInfo, | ||||||
|  | 				[Header.Metadata.BrowserUrls] = Urls, | ||||||
|  | 				[Header.Metadata.TriggerInfo] = TriggerInfo, | ||||||
|  | 				[Header.Metadata.WindowTitle] = WindowTitle | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			return json.ToString(Formatting.None); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void CaptureApplicationData() | ||||||
|  | 		{ | ||||||
|  | 			if (applicationMonitor.TryGetActiveApplication(out var application)) | ||||||
|  | 			{ | ||||||
|  | 				ApplicationInfo = BuildApplicationInfo(application); | ||||||
|  | 				WindowTitle = BuildWindowTitle(application); | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				ApplicationInfo = "-"; | ||||||
|  | 				WindowTitle = "-"; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private string BuildApplicationInfo(ActiveApplication application) | ||||||
|  | 		{ | ||||||
|  | 			var info = new StringBuilder(); | ||||||
|  | 
 | ||||||
|  | 			info.Append(application.Process.Name); | ||||||
|  | 
 | ||||||
|  | 			if (application.Process.OriginalName != default) | ||||||
|  | 			{ | ||||||
|  | 				info.Append($" ({application.Process.OriginalName}{(application.Process.Signature == default ? ")" : "")}"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (application.Process.Signature != default) | ||||||
|  | 			{ | ||||||
|  | 				info.Append($"{(application.Process.OriginalName == default ? "(" : ", ")}{application.Process.Signature})"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return info.ToString(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private string BuildWindowTitle(ActiveApplication application) | ||||||
|  | 		{ | ||||||
|  | 			return string.IsNullOrEmpty(application.Window.Title) ? "-" : application.Window.Title; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void CaptureBrowserData() | ||||||
|  | 		{ | ||||||
|  | 			var windows = browser.GetWindows(); | ||||||
|  | 
 | ||||||
|  | 			BrowserInfo = BuildBrowserInfo(windows); | ||||||
|  | 			Urls = BuildUrls(windows); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private string BuildUrls(IEnumerable<IBrowserWindow> windows) | ||||||
|  | 		{ | ||||||
|  | 			return string.Join(", ", windows.Select(w => w.Url)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private string BuildBrowserInfo(IEnumerable<IBrowserWindow> windows) | ||||||
|  | 		{ | ||||||
|  | 			return string.Join(", ", windows.Select(w => $"{(w.IsMainWindow ? "Main" : "Additional")} Window: {w.Title} ({w.Url})")); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void CaptureIntervalTrigger(IntervalTrigger interval) | ||||||
|  | 		{ | ||||||
|  | 			TriggerInfo = $"Maximum interval of {interval.ConfigurationValue}ms has been reached ({interval.TimeElapsed}ms)."; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void CaptureKeyboardTrigger(KeyboardTrigger keyboard) | ||||||
|  | 		{ | ||||||
|  | 			var flags = Enum.GetValues(typeof(KeyModifier)).OfType<KeyModifier>().Where(m => m != KeyModifier.None && keyboard.Modifier.HasFlag(m)); | ||||||
|  | 			var modifiers = flags.Any() ? string.Join(" + ", flags) + " + " : string.Empty; | ||||||
|  | 
 | ||||||
|  | 			TriggerInfo = $"'{modifiers}{keyboard.Key}' has been {keyboard.State.ToString().ToLower()}."; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void CaptureMouseTrigger(MouseTrigger mouse) | ||||||
|  | 		{ | ||||||
|  | 			if (mouse.Info.IsTouch) | ||||||
|  | 			{ | ||||||
|  | 				TriggerInfo = $"Tap as {mouse.Button} mouse button has been {mouse.State.ToString().ToLower()} at ({mouse.Info.X}/{mouse.Info.Y})."; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 				TriggerInfo = $"{mouse.Button} mouse button has been {mouse.State.ToString().ToLower()} at ({mouse.Info.X}/{mouse.Info.Y})."; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,19 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) 2023 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.WindowsApi.Contracts.Events; | ||||||
|  | 
 | ||||||
|  | namespace SafeExamBrowser.Proctoring.ScreenProctoring.Data | ||||||
|  | { | ||||||
|  | 	internal class MouseTrigger | ||||||
|  | 	{ | ||||||
|  | 		public MouseButton Button { get; internal set; } | ||||||
|  | 		public MouseInformation Info { get; internal set; } | ||||||
|  | 		public MouseButtonState State { get; internal set; } | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -11,10 +11,13 @@ using System.Threading; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using System.Timers; | using System.Timers; | ||||||
| using System.Windows.Input; | using System.Windows.Input; | ||||||
|  | using SafeExamBrowser.Browser.Contracts; | ||||||
| using SafeExamBrowser.Core.Contracts.Notifications.Events; | using SafeExamBrowser.Core.Contracts.Notifications.Events; | ||||||
| using SafeExamBrowser.Core.Contracts.Resources.Icons; | using SafeExamBrowser.Core.Contracts.Resources.Icons; | ||||||
| using SafeExamBrowser.I18n.Contracts; | using SafeExamBrowser.I18n.Contracts; | ||||||
| using SafeExamBrowser.Logging.Contracts; | using SafeExamBrowser.Logging.Contracts; | ||||||
|  | using SafeExamBrowser.Monitoring.Contracts.Applications; | ||||||
|  | using SafeExamBrowser.Proctoring.ScreenProctoring.Data; | ||||||
| using SafeExamBrowser.Proctoring.ScreenProctoring.Imaging; | using SafeExamBrowser.Proctoring.ScreenProctoring.Imaging; | ||||||
| using SafeExamBrowser.Proctoring.ScreenProctoring.Service; | using SafeExamBrowser.Proctoring.ScreenProctoring.Service; | ||||||
| using SafeExamBrowser.Server.Contracts.Events.Proctoring; | using SafeExamBrowser.Server.Contracts.Events.Proctoring; | ||||||
|  | @ -31,6 +34,8 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring | ||||||
| 	{ | 	{ | ||||||
| 		private readonly object @lock = new object(); | 		private readonly object @lock = new object(); | ||||||
| 
 | 
 | ||||||
|  | 		private readonly IApplicationMonitor applicationMonitor; | ||||||
|  | 		private readonly IBrowserApplication browser; | ||||||
| 		private readonly IModuleLogger logger; | 		private readonly IModuleLogger logger; | ||||||
| 		private readonly INativeMethods nativeMethods; | 		private readonly INativeMethods nativeMethods; | ||||||
| 		private readonly ServiceProxy service; | 		private readonly ServiceProxy service; | ||||||
|  | @ -47,12 +52,16 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring | ||||||
| 		public override event NotificationChangedEventHandler NotificationChanged; | 		public override event NotificationChangedEventHandler NotificationChanged; | ||||||
| 
 | 
 | ||||||
| 		internal ScreenProctoringImplementation( | 		internal ScreenProctoringImplementation( | ||||||
|  | 			IApplicationMonitor applicationMonitor, | ||||||
|  | 			IBrowserApplication browser, | ||||||
| 			IModuleLogger logger, | 			IModuleLogger logger, | ||||||
| 			INativeMethods nativeMethods, | 			INativeMethods nativeMethods, | ||||||
| 			ServiceProxy service, | 			ServiceProxy service, | ||||||
| 			ProctoringSettings settings, | 			ProctoringSettings settings, | ||||||
| 			IText text) | 			IText text) | ||||||
| 		{ | 		{ | ||||||
|  | 			this.applicationMonitor = applicationMonitor; | ||||||
|  | 			this.browser = browser; | ||||||
| 			this.logger = logger; | 			this.logger = logger; | ||||||
| 			this.nativeMethods = nativeMethods; | 			this.nativeMethods = nativeMethods; | ||||||
| 			this.service = service; | 			this.service = service; | ||||||
|  | @ -194,18 +203,28 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring | ||||||
| 
 | 
 | ||||||
| 		private bool KeyboardHookCallback(int keyCode, KeyModifier modifier, KeyState state) | 		private bool KeyboardHookCallback(int keyCode, KeyModifier modifier, KeyState state) | ||||||
| 		{ | 		{ | ||||||
| 			var key = KeyInterop.KeyFromVirtualKey(keyCode); | 			var trigger = new KeyboardTrigger | ||||||
|  | 			{ | ||||||
|  | 				Key = KeyInterop.KeyFromVirtualKey(keyCode), | ||||||
|  | 				Modifier = modifier, | ||||||
|  | 				State = state | ||||||
|  | 			}; | ||||||
| 
 | 
 | ||||||
| 			TryExecute(); | 			TryExecute(keyboard: trigger); | ||||||
| 
 | 
 | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private bool MouseHookCallback(MouseButton button, MouseButtonState state, MouseInformation info) | 		private bool MouseHookCallback(MouseButton button, MouseButtonState state, MouseInformation info) | ||||||
| 		{ | 		{ | ||||||
| 			var isTouch = info.IsTouch; | 			var trigger = new MouseTrigger | ||||||
|  | 			{ | ||||||
|  | 				Button = button, | ||||||
|  | 				Info = info, | ||||||
|  | 				State = state | ||||||
|  | 			}; | ||||||
| 
 | 
 | ||||||
| 			TryExecute(); | 			TryExecute(mouse: trigger); | ||||||
| 
 | 
 | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
|  | @ -238,10 +257,16 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring | ||||||
| 
 | 
 | ||||||
| 		private void Timer_Elapsed(object sender, ElapsedEventArgs args) | 		private void Timer_Elapsed(object sender, ElapsedEventArgs args) | ||||||
| 		{ | 		{ | ||||||
| 			TryExecute(); | 			var trigger = new IntervalTrigger | ||||||
|  | 			{ | ||||||
|  | 				ConfigurationValue = settings.MaxInterval, | ||||||
|  | 				TimeElapsed = Convert.ToInt32(DateTime.Now.Subtract(last).TotalMilliseconds) | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			TryExecute(interval: trigger); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private void TryExecute() | 		private void TryExecute(IntervalTrigger interval = default, KeyboardTrigger keyboard = default, MouseTrigger mouse = default) | ||||||
| 		{ | 		{ | ||||||
| 			if (MinimumIntervalElapsed() && Monitor.TryEnter(@lock)) | 			if (MinimumIntervalElapsed() && Monitor.TryEnter(@lock)) | ||||||
| 			{ | 			{ | ||||||
|  | @ -252,16 +277,27 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring | ||||||
| 				{ | 				{ | ||||||
| 					try | 					try | ||||||
| 					{ | 					{ | ||||||
|  | 						var metadata = new Metadata(applicationMonitor, browser, logger.CloneFor(nameof(Metadata))); | ||||||
|  | 
 | ||||||
| 						using (var screenShot = new ScreenShot(logger.CloneFor(nameof(ScreenShot)), settings)) | 						using (var screenShot = new ScreenShot(logger.CloneFor(nameof(ScreenShot)), settings)) | ||||||
| 						{ | 						{ | ||||||
|  | 							metadata.Capture(interval, keyboard, mouse); | ||||||
| 							screenShot.Take(); | 							screenShot.Take(); | ||||||
| 							screenShot.Compress(); | 							screenShot.Compress(); | ||||||
| 							service.Send(screenShot); | 
 | ||||||
|  | 							if (service.IsConnected) | ||||||
|  | 							{ | ||||||
|  | 								service.Send(metadata, screenShot); | ||||||
|  | 							} | ||||||
|  | 							else | ||||||
|  | 							{ | ||||||
|  | 								logger.Warn("Cannot send screen shot as service is disconnected!"); | ||||||
|  | 							} | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 					catch (Exception e) | 					catch (Exception e) | ||||||
| 					{ | 					{ | ||||||
| 						logger.Error("Failed to process screen shot!", e); | 						logger.Error("Failed to execute capturing and/or transmission!", e); | ||||||
| 					} | 					} | ||||||
| 				}); | 				}); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,6 +13,18 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests | ||||||
| 		internal const string ACCEPT = "Accept"; | 		internal const string ACCEPT = "Accept"; | ||||||
| 		internal const string AUTHORIZATION = "Authorization"; | 		internal const string AUTHORIZATION = "Authorization"; | ||||||
| 		internal const string GROUP_ID = "SEB_GROUP_UUID"; | 		internal const string GROUP_ID = "SEB_GROUP_UUID"; | ||||||
|  | 		internal const string IMAGE_FORMAT = "imageFormat"; | ||||||
|  | 		internal const string METADATA = "metaData"; | ||||||
| 		internal const string SESSION_ID = "SEB_SESSION_UUID"; | 		internal const string SESSION_ID = "SEB_SESSION_UUID"; | ||||||
|  | 		internal const string TIMESTAMP = "timestamp"; | ||||||
|  | 
 | ||||||
|  | 		internal static class Metadata | ||||||
|  | 		{ | ||||||
|  | 			internal const string ApplicationInfo = "screenProctoringMetadataApplication"; | ||||||
|  | 			internal const string BrowserInfo = "screenProctoringMetadataBrowser"; | ||||||
|  | 			internal const string BrowserUrls = "screenProctoringMetadataURL"; | ||||||
|  | 			internal const string TriggerInfo = "screenProctoringMetadataUserAction"; | ||||||
|  | 			internal const string WindowTitle = "screenProctoringMetadataWindowTitle"; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| using System; | using System; | ||||||
| using System.Net.Http; | using System.Net.Http; | ||||||
| using SafeExamBrowser.Logging.Contracts; | using SafeExamBrowser.Logging.Contracts; | ||||||
|  | using SafeExamBrowser.Proctoring.ScreenProctoring.Data; | ||||||
| using SafeExamBrowser.Proctoring.ScreenProctoring.Imaging; | using SafeExamBrowser.Proctoring.ScreenProctoring.Imaging; | ||||||
| using SafeExamBrowser.Settings.Proctoring; | using SafeExamBrowser.Settings.Proctoring; | ||||||
| 
 | 
 | ||||||
|  | @ -20,12 +21,13 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests | ||||||
| 		{ | 		{ | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		internal bool TryExecute(ScreenShot screenShot, string sessionId, out string message) | 		internal bool TryExecute(Metadata metadata, ScreenShot screenShot, string sessionId, out string message) | ||||||
| 		{ | 		{ | ||||||
| 			var imageFormat = ("imageFormat", ToString(screenShot.Format)); | 			var data = (Header.METADATA, metadata.ToJson()); | ||||||
| 			var timestamp = ("timestamp", DateTime.Now.ToUnixTimestamp().ToString()); | 			var imageFormat = (Header.IMAGE_FORMAT, ToString(screenShot.Format)); | ||||||
|  | 			var timestamp = (Header.TIMESTAMP, DateTime.Now.ToUnixTimestamp().ToString()); | ||||||
| 			var url = api.ScreenShotEndpoint.Replace(Api.SESSION_ID, sessionId); | 			var url = api.ScreenShotEndpoint.Replace(Api.SESSION_ID, sessionId); | ||||||
| 			var success = TryExecute(HttpMethod.Post, url, out var response, screenShot.Data, ContentType.OCTET_STREAM, Authorization, imageFormat, timestamp); | 			var success = TryExecute(HttpMethod.Post, url, out var response, screenShot.Data, ContentType.OCTET_STREAM, Authorization, data, imageFormat, timestamp); | ||||||
| 
 | 
 | ||||||
| 			message = response.ToLogString(); | 			message = response.ToLogString(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| using System; | using System; | ||||||
| using System.Net.Http; | using System.Net.Http; | ||||||
| using SafeExamBrowser.Logging.Contracts; | using SafeExamBrowser.Logging.Contracts; | ||||||
|  | using SafeExamBrowser.Proctoring.ScreenProctoring.Data; | ||||||
| using SafeExamBrowser.Proctoring.ScreenProctoring.Imaging; | using SafeExamBrowser.Proctoring.ScreenProctoring.Imaging; | ||||||
| using SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests; | using SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests; | ||||||
| 
 | 
 | ||||||
|  | @ -69,10 +70,10 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service | ||||||
| 			return new ServiceResponse(success, message); | 			return new ServiceResponse(success, message); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		internal ServiceResponse Send(ScreenShot screenShot) | 		internal ServiceResponse Send(Metadata metadata, ScreenShot screenShot) | ||||||
| 		{ | 		{ | ||||||
| 			var request = new ScreenShotRequest(api, httpClient, logger, parser); | 			var request = new ScreenShotRequest(api, httpClient, logger, parser); | ||||||
| 			var success = request.TryExecute(screenShot, SessionId, out var message); | 			var success = request.TryExecute(metadata, screenShot, SessionId, out var message); | ||||||
| 
 | 
 | ||||||
| 			if (success) | 			if (success) | ||||||
| 			{ | 			{ | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ namespace SafeExamBrowser.UserInterface.Contracts | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Creates an application control for the specified application and location. | 		/// Creates an application control for the specified application and location. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		IApplicationControl CreateApplicationControl(IApplication application, Location location); | 		IApplicationControl CreateApplicationControl(IApplication<IApplicationWindow> application, Location location); | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Creates a system control which allows to change the audio settings of the computer. | 		/// Creates a system control which allows to change the audio settings of the computer. | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ namespace SafeExamBrowser.UserInterface.Contracts.Shell | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Adds the given application to the task view. | 		/// Adds the given application to the task view. | ||||||
| 		/// </summary> | 		/// </summary> | ||||||
| 		void Add(IApplication application); | 		void Add(IApplication<IApplicationWindow> application); | ||||||
| 
 | 
 | ||||||
| 		/// <summary> | 		/// <summary> | ||||||
| 		/// Registers the specified activator for the task view. | 		/// Registers the specified activator for the task view. | ||||||
|  |  | ||||||
|  | @ -17,12 +17,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter | ||||||
| { | { | ||||||
| 	internal partial class ApplicationButton : UserControl | 	internal partial class ApplicationButton : UserControl | ||||||
| 	{ | 	{ | ||||||
| 		private readonly IApplication application; | 		private readonly IApplication<IApplicationWindow> application; | ||||||
| 		private readonly IApplicationWindow window; | 		private readonly IApplicationWindow window; | ||||||
| 
 | 
 | ||||||
| 		internal event EventHandler Clicked; | 		internal event EventHandler Clicked; | ||||||
| 
 | 
 | ||||||
| 		internal ApplicationButton(IApplication application, IApplicationWindow window = null) | 		internal ApplicationButton(IApplication<IApplicationWindow> application, IApplicationWindow window = null) | ||||||
| 		{ | 		{ | ||||||
| 			this.application = application; | 			this.application = application; | ||||||
| 			this.window = window; | 			this.window = window; | ||||||
|  |  | ||||||
|  | @ -15,9 +15,9 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter | ||||||
| { | { | ||||||
| 	internal partial class ApplicationControl : UserControl, IApplicationControl | 	internal partial class ApplicationControl : UserControl, IApplicationControl | ||||||
| 	{ | 	{ | ||||||
| 		private IApplication application; | 		private readonly IApplication<IApplicationWindow> application; | ||||||
| 
 | 
 | ||||||
| 		internal ApplicationControl(IApplication application) | 		internal ApplicationControl(IApplication<IApplicationWindow> application) | ||||||
| 		{ | 		{ | ||||||
| 			this.application = application; | 			this.application = application; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,10 +22,10 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar | ||||||
| { | { | ||||||
| 	internal partial class ApplicationControl : UserControl, IApplicationControl | 	internal partial class ApplicationControl : UserControl, IApplicationControl | ||||||
| 	{ | 	{ | ||||||
| 		private readonly IApplication application; | 		private readonly IApplication<IApplicationWindow> application; | ||||||
| 		private IApplicationWindow single; | 		private IApplicationWindow single; | ||||||
| 
 | 
 | ||||||
| 		internal ApplicationControl(IApplication application) | 		internal ApplicationControl(IApplication<IApplicationWindow> application) | ||||||
| 		{ | 		{ | ||||||
| 			this.application = application; | 			this.application = application; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ namespace SafeExamBrowser.UserInterface.Desktop | ||||||
| 			return new ActionCenter(); | 			return new ActionCenter(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public IApplicationControl CreateApplicationControl(IApplication application, Location location) | 		public IApplicationControl CreateApplicationControl(IApplication<IApplicationWindow> application, Location location) | ||||||
| 		{ | 		{ | ||||||
| 			if (location == Location.ActionCenter) | 			if (location == Location.ActionCenter) | ||||||
| 			{ | 			{ | ||||||
|  |  | ||||||
|  | @ -20,22 +20,22 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows | ||||||
| { | { | ||||||
| 	internal partial class Taskview : Window, ITaskview | 	internal partial class Taskview : Window, ITaskview | ||||||
| 	{ | 	{ | ||||||
| 		private IList<IApplication> applications; | 		private readonly IList<IApplication<IApplicationWindow>> applications; | ||||||
| 		private LinkedListNode<WindowControl> current; | 		private readonly LinkedList<WindowControl> controls; | ||||||
| 		private LinkedList<WindowControl> controls; |  | ||||||
| 
 | 
 | ||||||
|  | 		private LinkedListNode<WindowControl> current; | ||||||
| 		internal IntPtr Handle { get; private set; } | 		internal IntPtr Handle { get; private set; } | ||||||
| 
 | 
 | ||||||
| 		internal Taskview() | 		internal Taskview() | ||||||
| 		{ | 		{ | ||||||
| 			applications = new List<IApplication>(); | 			applications = new List<IApplication<IApplicationWindow>>(); | ||||||
| 			controls = new LinkedList<WindowControl>(); | 			controls = new LinkedList<WindowControl>(); | ||||||
| 
 | 
 | ||||||
| 			InitializeComponent(); | 			InitializeComponent(); | ||||||
| 			InitializeTaskview(); | 			InitializeTaskview(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public void Add(IApplication application) | 		public void Add(IApplication<IApplicationWindow> application) | ||||||
| 		{ | 		{ | ||||||
| 			application.WindowsChanged += Application_WindowsChanged; | 			application.WindowsChanged += Application_WindowsChanged; | ||||||
| 			applications.Add(application); | 			applications.Add(application); | ||||||
|  |  | ||||||
|  | @ -17,12 +17,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter | ||||||
| { | { | ||||||
| 	internal partial class ApplicationButton : UserControl | 	internal partial class ApplicationButton : UserControl | ||||||
| 	{ | 	{ | ||||||
| 		private readonly IApplication application; | 		private readonly IApplication<IApplicationWindow> application; | ||||||
| 		private readonly IApplicationWindow window; | 		private readonly IApplicationWindow window; | ||||||
| 
 | 
 | ||||||
| 		internal event EventHandler Clicked; | 		internal event EventHandler Clicked; | ||||||
| 
 | 
 | ||||||
| 		internal ApplicationButton(IApplication application, IApplicationWindow window = null) | 		internal ApplicationButton(IApplication<IApplicationWindow> application, IApplicationWindow window = null) | ||||||
| 		{ | 		{ | ||||||
| 			this.application = application; | 			this.application = application; | ||||||
| 			this.window = window; | 			this.window = window; | ||||||
|  |  | ||||||
|  | @ -15,9 +15,9 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter | ||||||
| { | { | ||||||
| 	internal partial class ApplicationControl : UserControl, IApplicationControl | 	internal partial class ApplicationControl : UserControl, IApplicationControl | ||||||
| 	{ | 	{ | ||||||
| 		private IApplication application; | 		private readonly IApplication<IApplicationWindow> application; | ||||||
| 
 | 
 | ||||||
| 		internal ApplicationControl(IApplication application) | 		internal ApplicationControl(IApplication<IApplicationWindow> application) | ||||||
| 		{ | 		{ | ||||||
| 			this.application = application; | 			this.application = application; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,10 +22,10 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar | ||||||
| { | { | ||||||
| 	internal partial class ApplicationControl : UserControl, IApplicationControl | 	internal partial class ApplicationControl : UserControl, IApplicationControl | ||||||
| 	{ | 	{ | ||||||
| 		private readonly IApplication application; | 		private readonly IApplication<IApplicationWindow> application; | ||||||
| 		private IApplicationWindow single; | 		private IApplicationWindow single; | ||||||
| 
 | 
 | ||||||
| 		internal ApplicationControl(IApplication application) | 		internal ApplicationControl(IApplication<IApplicationWindow> application) | ||||||
| 		{ | 		{ | ||||||
| 			this.application = application; | 			this.application = application; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ namespace SafeExamBrowser.UserInterface.Mobile | ||||||
| 			return new ActionCenter(); | 			return new ActionCenter(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public IApplicationControl CreateApplicationControl(IApplication application, Location location) | 		public IApplicationControl CreateApplicationControl(IApplication<IApplicationWindow> application, Location location) | ||||||
| 		{ | 		{ | ||||||
| 			if (location == Location.ActionCenter) | 			if (location == Location.ActionCenter) | ||||||
| 			{ | 			{ | ||||||
|  |  | ||||||
|  | @ -20,22 +20,23 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows | ||||||
| { | { | ||||||
| 	internal partial class Taskview : Window, ITaskview | 	internal partial class Taskview : Window, ITaskview | ||||||
| 	{ | 	{ | ||||||
| 		private IList<IApplication> applications; | 		private readonly IList<IApplication<IApplicationWindow>> applications; | ||||||
|  | 		private readonly LinkedList<WindowControl> controls; | ||||||
|  | 
 | ||||||
| 		private LinkedListNode<WindowControl> current; | 		private LinkedListNode<WindowControl> current; | ||||||
| 		private LinkedList<WindowControl> controls; |  | ||||||
| 
 | 
 | ||||||
| 		internal IntPtr Handle { get; private set; } | 		internal IntPtr Handle { get; private set; } | ||||||
| 
 | 
 | ||||||
| 		internal Taskview() | 		internal Taskview() | ||||||
| 		{ | 		{ | ||||||
| 			applications = new List<IApplication>(); | 			applications = new List<IApplication<IApplicationWindow>>(); | ||||||
| 			controls = new LinkedList<WindowControl>(); | 			controls = new LinkedList<WindowControl>(); | ||||||
| 
 | 
 | ||||||
| 			InitializeComponent(); | 			InitializeComponent(); | ||||||
| 			InitializeTaskview(); | 			InitializeTaskview(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		public void Add(IApplication application) | 		public void Add(IApplication<IApplicationWindow> application) | ||||||
| 		{ | 		{ | ||||||
| 			application.WindowsChanged += Application_WindowsChanged; | 			application.WindowsChanged += Application_WindowsChanged; | ||||||
| 			applications.Add(application); | 			applications.Add(application); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Damian Büchel
						Damian Büchel