Restore SEBPatch
This commit is contained in:
20
SafeExamBrowser.Monitoring/Display/Bounds.cs
Normal file
20
SafeExamBrowser.Monitoring/Display/Bounds.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Display
|
||||
{
|
||||
internal class Bounds : IBounds
|
||||
{
|
||||
public int Left { get; set; }
|
||||
public int Top { get; set; }
|
||||
public int Right { get; set; }
|
||||
public int Bottom { get; set; }
|
||||
}
|
||||
}
|
18
SafeExamBrowser.Monitoring/Display/Display.cs
Normal file
18
SafeExamBrowser.Monitoring/Display/Display.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Monitoring.Display
|
||||
{
|
||||
internal class Display
|
||||
{
|
||||
public string Identifier { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public bool IsInternal => Technology == VideoOutputTechnology.DisplayPortEmbedded || Technology == VideoOutputTechnology.Internal;
|
||||
public VideoOutputTechnology Technology { get; set; } = VideoOutputTechnology.Uninitialized;
|
||||
}
|
||||
}
|
243
SafeExamBrowser.Monitoring/Display/DisplayMonitor.cs
Normal file
243
SafeExamBrowser.Monitoring/Display/DisplayMonitor.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Management;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Display;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Display.Events;
|
||||
using SafeExamBrowser.Settings.Monitoring;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
using OperatingSystem = SafeExamBrowser.SystemComponents.Contracts.OperatingSystem;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.Display
|
||||
{
|
||||
public class DisplayMonitor : IDisplayMonitor
|
||||
{
|
||||
private IBounds originalWorkingArea;
|
||||
private readonly ILogger logger;
|
||||
private readonly INativeMethods nativeMethods;
|
||||
private readonly ISystemInfo systemInfo;
|
||||
private string wallpaper;
|
||||
|
||||
public event DisplayChangedEventHandler DisplayChanged;
|
||||
|
||||
public DisplayMonitor(ILogger logger, INativeMethods nativeMethods, ISystemInfo systemInfo)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.systemInfo = systemInfo;
|
||||
}
|
||||
|
||||
public void InitializePrimaryDisplay(int taskbarHeight)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void ResetPrimaryDisplay()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void StartMonitoringDisplayChanges()
|
||||
{
|
||||
SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged;
|
||||
logger.Info("Started monitoring display changes.");
|
||||
}
|
||||
|
||||
public void StopMonitoringDisplayChanges()
|
||||
{
|
||||
SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged;
|
||||
logger.Info("Stopped monitoring display changes.");
|
||||
}
|
||||
|
||||
public ValidationResult ValidateConfiguration(DisplaySettings settings)
|
||||
{
|
||||
var result = new ValidationResult();
|
||||
|
||||
if (TryLoadDisplays(out var displays))
|
||||
{
|
||||
var active = displays.Where(d => d.IsActive);
|
||||
var count = active.Count();
|
||||
|
||||
result.ExternalDisplays = active.Count(d => !d.IsInternal);
|
||||
result.InternalDisplays = active.Count(d => d.IsInternal);
|
||||
result.IsAllowed = true;
|
||||
|
||||
if (result.IsAllowed)
|
||||
{
|
||||
logger.Info($"Detected 1 active displays, 1 are allowed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info($"Detected 1 active displays, 1 are allowed!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.IsAllowed = settings.IgnoreError;
|
||||
logger.Warn($"Failed to validate display configuration, {(result.IsAllowed ? "ignoring error" : "active configuration is not allowed")}.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void InitializeWorkingArea(int taskbarHeight)
|
||||
{
|
||||
var identifier = GetIdentifierForPrimaryDisplay();
|
||||
|
||||
if (originalWorkingArea == null)
|
||||
{
|
||||
originalWorkingArea = nativeMethods.GetWorkingArea();
|
||||
LogWorkingArea($"Saved original working area for {identifier}", originalWorkingArea);
|
||||
}
|
||||
|
||||
var area = new Bounds
|
||||
{
|
||||
Left = 0,
|
||||
Top = 0,
|
||||
Right = Screen.PrimaryScreen.Bounds.Width,
|
||||
Bottom = Screen.PrimaryScreen.Bounds.Height - taskbarHeight
|
||||
};
|
||||
|
||||
LogWorkingArea($"Trying to set new working area for {identifier}", area);
|
||||
nativeMethods.SetWorkingArea(area);
|
||||
LogWorkingArea($"Working area of {identifier} is now set to", nativeMethods.GetWorkingArea());
|
||||
}
|
||||
|
||||
private void InitializeWallpaper()
|
||||
{
|
||||
if (systemInfo.OperatingSystem == OperatingSystem.Windows7)
|
||||
{
|
||||
var path = nativeMethods.GetWallpaperPath();
|
||||
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
wallpaper = path;
|
||||
logger.Info($"Saved wallpaper image: {wallpaper}.");
|
||||
}
|
||||
|
||||
nativeMethods.RemoveWallpaper();
|
||||
logger.Info("Removed current wallpaper.");
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryLoadDisplays(out IList<Display> displays)
|
||||
{
|
||||
var success = true;
|
||||
|
||||
displays = new List<Display>();
|
||||
|
||||
try
|
||||
{
|
||||
using (var searcher = new ManagementObjectSearcher(@"Root\WMI", "SELECT * FROM WmiMonitorBasicDisplayParams"))
|
||||
using (var results = searcher.Get())
|
||||
{
|
||||
var displayParameters = results.Cast<ManagementObject>();
|
||||
|
||||
foreach (var display in displayParameters)
|
||||
{
|
||||
displays.Add(new Display
|
||||
{
|
||||
Identifier = Convert.ToString(display["InstanceName"]),
|
||||
IsActive = Convert.ToBoolean(display["Active"])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
using (var searcher = new ManagementObjectSearcher(@"Root\WMI", "SELECT * FROM WmiMonitorConnectionParams"))
|
||||
using (var results = searcher.Get())
|
||||
{
|
||||
var connectionParameters = results.Cast<ManagementObject>();
|
||||
|
||||
foreach (var connection in connectionParameters)
|
||||
{
|
||||
var identifier = Convert.ToString(connection["InstanceName"]);
|
||||
var isActive = Convert.ToBoolean(connection["Active"]);
|
||||
var technologyValue = Convert.ToInt64(connection["VideoOutputTechnology"]);
|
||||
var technology = (VideoOutputTechnology) technologyValue;
|
||||
var display = displays.FirstOrDefault(d => d.Identifier?.Equals(identifier, StringComparison.OrdinalIgnoreCase) == true);
|
||||
|
||||
if (!Enum.IsDefined(typeof(VideoOutputTechnology), technology))
|
||||
{
|
||||
logger.Warn($"Detected undefined video output technology '{technologyValue}' for display '{identifier}'!");
|
||||
}
|
||||
|
||||
if (display != default(Display))
|
||||
{
|
||||
display.IsActive &= isActive;
|
||||
display.Technology = technology;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
success = false;
|
||||
logger.Error("Failed to query displays!", e);
|
||||
}
|
||||
|
||||
foreach (var display in displays)
|
||||
{
|
||||
logger.Info($"Detected {(display.IsActive ? "active" : "inactive")}, {(display.IsInternal ? "internal" : "external")} display '{display.Identifier}' connected via '{display.Technology}'.");
|
||||
}
|
||||
|
||||
//return success;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ResetWorkingArea()
|
||||
{
|
||||
var identifier = GetIdentifierForPrimaryDisplay();
|
||||
|
||||
if (originalWorkingArea != null)
|
||||
{
|
||||
nativeMethods.SetWorkingArea(originalWorkingArea);
|
||||
LogWorkingArea($"Restored original working area for {identifier}", originalWorkingArea);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"Could not restore original working area for {identifier}!");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetWallpaper()
|
||||
{
|
||||
if (systemInfo.OperatingSystem == OperatingSystem.Windows7 && !String.IsNullOrEmpty(wallpaper))
|
||||
{
|
||||
nativeMethods.SetWallpaper(wallpaper);
|
||||
logger.Info($"Restored wallpaper image: {wallpaper}.");
|
||||
}
|
||||
}
|
||||
|
||||
private string GetIdentifierForPrimaryDisplay()
|
||||
{
|
||||
var name = Screen.PrimaryScreen.DeviceName?.Replace(@"\\.\", string.Empty);
|
||||
var resolution = $"{Screen.PrimaryScreen.Bounds.Width}x{Screen.PrimaryScreen.Bounds.Height}";
|
||||
var identifier = $"{name} ({resolution})";
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
private void LogWorkingArea(string message, IBounds area)
|
||||
{
|
||||
logger.Info($"{message}: Left = {area.Left}, Top = {area.Top}, Right = {area.Right}, Bottom = {area.Bottom}.");
|
||||
}
|
||||
}
|
||||
}
|
41
SafeExamBrowser.Monitoring/Display/VideoOutputTechnology.cs
Normal file
41
SafeExamBrowser.Monitoring/Display/VideoOutputTechnology.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2024 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.Monitoring.Display
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmdt/ne-d3dkmdt-_d3dkmdt_video_output_technology
|
||||
/// </remarks>
|
||||
internal enum VideoOutputTechnology : long
|
||||
{
|
||||
Uninitialized = -2,
|
||||
Other = -1,
|
||||
HD15 = 0,
|
||||
SVideo = 1,
|
||||
CompositeVideo = 2,
|
||||
ComponentVideo = 3,
|
||||
DVI = 4,
|
||||
HDMI = 5,
|
||||
LVDS = 6,
|
||||
DJPN = 8,
|
||||
SDI = 9,
|
||||
DisplayPortExternal = 10,
|
||||
DisplayPortEmbedded = 11,
|
||||
UDIExternal = 12,
|
||||
UDIEmbedded = 13,
|
||||
SDTVDongle = 14,
|
||||
MiraCast = 15,
|
||||
IndirectWired = 16,
|
||||
Internal = 0x80000000,
|
||||
SVideo4Pin = SVideo,
|
||||
SVideo7Pin = SVideo,
|
||||
RF = ComponentVideo,
|
||||
RCA3Component = ComponentVideo,
|
||||
BNC = ComponentVideo
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user