DisplayMagician/DisplayMagicianShared/Windows/WinLibrary.cs

1125 lines
75 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
using DisplayMagicianShared;
using System.ComponentModel;
using System.Text.RegularExpressions;
namespace DisplayMagicianShared.Windows
{
[StructLayout(LayoutKind.Sequential)]
public struct ADVANCED_HDR_INFO_PER_PATH : IEquatable<ADVANCED_HDR_INFO_PER_PATH>
{
public LUID AdapterId;
public uint Id;
public DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO AdvancedColorInfo;
public DISPLAYCONFIG_SDR_WHITE_LEVEL SDRWhiteLevel;
public bool Equals(ADVANCED_HDR_INFO_PER_PATH other)
=> // AdapterId.Equals(other.AdapterId) && // Removed the AdapterId from the Equals, as it changes after reboot.
Id == other.Id &&
AdvancedColorInfo.Equals(other.AdvancedColorInfo) &&
SDRWhiteLevel.Equals(other.SDRWhiteLevel);
public override int GetHashCode()
{
return (Id, AdvancedColorInfo, SDRWhiteLevel).GetHashCode();
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWS_DISPLAY_CONFIG : IEquatable<WINDOWS_DISPLAY_CONFIG>
{
public Dictionary<ulong, string> DisplayAdapters;
public DISPLAYCONFIG_PATH_INFO[] DisplayConfigPaths;
public DISPLAYCONFIG_MODE_INFO[] DisplayConfigModes;
public ADVANCED_HDR_INFO_PER_PATH[] DisplayHDRStates;
public List<string> DisplayIdentifiers;
public bool Equals(WINDOWS_DISPLAY_CONFIG other)
=> DisplayConfigPaths.SequenceEqual(other.DisplayConfigPaths) &&
DisplayConfigModes.SequenceEqual(other.DisplayConfigModes) &&
DisplayHDRStates.SequenceEqual(other.DisplayHDRStates);
public override int GetHashCode()
{
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates).GetHashCode();
}
}
class WinLibrary : IDisposable
{
// Static members are 'eagerly initialized', that is,
// immediately when class is loaded for the first time.
// .NET guarantees thread safety for static initialization
private static WinLibrary _instance = new WinLibrary();
private bool _initialised = false;
// To detect redundant calls
private bool _disposed = false;
// Instantiate a SafeHandle instance.
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
private IntPtr _adlContextHandle = IntPtr.Zero;
static WinLibrary() { }
public WinLibrary()
{
SharedLogger.logger.Trace("WinLibrary/WinLibrary: Intialising Windows CCD library interface");
_initialised = true;
}
~WinLibrary()
{
// The WinLibrary was initialised, but doesn't need to be freed.
SharedLogger.logger.Trace("WinLibrary/~WinLibrary: Destroying Windows CCD library interface");
}
// Public implementation of Dispose pattern callable by consumers.
public void Dispose() => Dispose(true);
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// Dispose managed state (managed objects).
_safeHandle?.Dispose();
}
_disposed = true;
}
public bool IsInstalled
{
get
{
return _initialised;
}
}
public static WinLibrary GetLibrary()
{
return _instance;
}
private void PatchAdapterIDs(ref WINDOWS_DISPLAY_CONFIG savedDisplayConfig, Dictionary<ulong, string> currentAdapterMap)
{
Dictionary<ulong, ulong> adapterOldToNewMap = new Dictionary<ulong, ulong>();
SharedLogger.logger.Trace("WinLibrary/PatchAdapterIDs: Going through the list of adapters we stored in the config to figure out the old adapterIDs");
foreach (KeyValuePair<ulong, string> savedAdapter in savedDisplayConfig.DisplayAdapters)
{
foreach (KeyValuePair<ulong, string> currentAdapter in currentAdapterMap)
{
if (currentAdapter.Value.Equals(savedAdapter.Value))
{
// we have found the new LUID Value for the same adapter
// So we want to store it
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: We found that saved adapter {savedAdapter.Key} has now been assigned adapter id {currentAdapter.Key} (AdapterName is {savedAdapter.Value})");
adapterOldToNewMap.Add(savedAdapter.Key, currentAdapter.Key);
}
}
}
ulong newAdapterValue = 0;
// Update the paths with the current adapter id
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display config paths to update the adapter id");
for (int i = 0; i < savedDisplayConfig.DisplayConfigPaths.Length; i++)
{
// Change the Path SourceInfo and TargetInfo AdapterIDs
if (adapterOldToNewMap.ContainsKey(savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value))
{
// We get here if there is a matching adapter
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value];
savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayConfigPaths[i].TargetInfo.AdapterId.Value];
savedDisplayConfig.DisplayConfigPaths[i].TargetInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
}
else
{
// if there isn't a matching adapter, then we just pick the first current one and hope that works!
// (it is highly likely to... its only if the user has multiple graphics cards with some weird config it may break)
newAdapterValue = currentAdapterMap.First().Key;
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
savedDisplayConfig.DisplayConfigPaths[i].TargetInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
}
}
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display config modes to update the adapter id");
// Update the modes with the current adapter id
for (int i = 0; i < savedDisplayConfig.DisplayConfigModes.Length; i++)
{
// Change the Mode AdapterID
if (adapterOldToNewMap.ContainsKey(savedDisplayConfig.DisplayConfigModes[i].AdapterId.Value))
{
// We get here if there is a matching adapter
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayConfigModes[i].AdapterId.Value];
savedDisplayConfig.DisplayConfigModes[i].AdapterId = AdapterValueToLUID(newAdapterValue);
}
else
{
// if there isn't a matching adapter, then we just pick the first current one and hope that works!
// (it is highly likely to... its only if the user has multiple graphics cards with some weird config it may break)
newAdapterValue = currentAdapterMap.First().Key;
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayConfigModes[i].AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
savedDisplayConfig.DisplayConfigModes[i].AdapterId = AdapterValueToLUID(newAdapterValue);
}
}
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display config HDR info to update the adapter id");
// Update the HDRInfo with the current adapter id
for (int i = 0; i < savedDisplayConfig.DisplayHDRStates.Length; i++)
{
// Change the Mode AdapterID
if (adapterOldToNewMap.ContainsKey(savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value))
{
// We get here if there is a matching adapter
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value];
savedDisplayConfig.DisplayHDRStates[i].AdapterId = AdapterValueToLUID(newAdapterValue);
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayHDRStates[i].AdvancedColorInfo.Header.AdapterId.Value];
savedDisplayConfig.DisplayHDRStates[i].AdvancedColorInfo.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayHDRStates[i].SDRWhiteLevel.Header.AdapterId.Value];
savedDisplayConfig.DisplayHDRStates[i].SDRWhiteLevel.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
}
else
{
// if there isn't a matching adapter, then we just pick the first current one and hope that works!
// (it is highly likely to... its only if the user has multiple graphics cards with some weird config it may break)
newAdapterValue = currentAdapterMap.First().Key;
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
savedDisplayConfig.DisplayHDRStates[i].AdapterId = AdapterValueToLUID(newAdapterValue);
savedDisplayConfig.DisplayHDRStates[i].AdvancedColorInfo.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
savedDisplayConfig.DisplayHDRStates[i].SDRWhiteLevel.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
}
}
}
public WINDOWS_DISPLAY_CONFIG GetActiveConfig()
{
SharedLogger.logger.Trace($"WinLibrary/GetActiveConfig: Getting the currently active config");
return GetWindowsDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS);
}
private WINDOWS_DISPLAY_CONFIG GetWindowsDisplayConfig(QDC selector = QDC.QDC_ONLY_ACTIVE_PATHS)
{
// Get the size of the largest Active Paths and Modes arrays
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the size of the largest Active Paths and Modes arrays");
int pathCount = 0;
int modeCount = 0;
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
}
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Config path and mode arrays");
var paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
var modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: The displays were modified between GetDisplayConfigBufferSizes and QueryDisplayConfig so we need to get the buffer sizes again.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the size of the largest Active Paths and Modes arrays");
// Screen changed in between GetDisplayConfigBufferSizes and QueryDisplayConfig, so we need to get buffer sizes again
// as per https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-querydisplayconfig
err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
}
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Config path and mode arrays");
paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: ERROR - The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
throw new WinLibraryException($"The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again.");
}
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays.");
}
// Prepare the empty windows display config
WINDOWS_DISPLAY_CONFIG windowsDisplayConfig = new WINDOWS_DISPLAY_CONFIG();
windowsDisplayConfig.DisplayAdapters = new Dictionary<ulong, string>();
windowsDisplayConfig.DisplayHDRStates = new ADVANCED_HDR_INFO_PER_PATH[pathCount];
// Now cycle through the paths and grab the HDR state information
// and map the adapter name to adapter id
var hdrInfos = new ADVANCED_HDR_INFO_PER_PATH[pathCount];
int hdrInfoCount = 0;
foreach (var path in paths)
{
// Get adapter ID for later
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get adapter name for adapter {path.TargetInfo.AdapterId.Value}.");
if (!windowsDisplayConfig.DisplayAdapters.ContainsKey(path.TargetInfo.AdapterId.Value))
{
var adapterInfo = new DISPLAYCONFIG_ADAPTER_NAME();
adapterInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME;
adapterInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_ADAPTER_NAME>();
adapterInfo.Header.AdapterId = path.TargetInfo.AdapterId;
adapterInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref adapterInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
// Store it for later
windowsDisplayConfig.DisplayAdapters.Add(path.TargetInfo.AdapterId.Value, adapterInfo.AdapterDevicePath);
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found adapter name {adapterInfo.AdapterDevicePath} for adapter {path.TargetInfo.AdapterId.Value}.");
}
else
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: ERROR - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to query the adapter name for adapter {path.TargetInfo.AdapterId.Value}.");
}
}
// Get advanced color info
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get advanced color info for display {path.TargetInfo.Id}.");
var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
colorInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
colorInfo.Header.AdapterId = path.TargetInfo.AdapterId;
colorInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref colorInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found color info for display {path.TargetInfo.Id}.");
if (colorInfo.AdvancedColorSupported)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is supported for display {path.TargetInfo.Id}.");
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is NOT supported for display {path.TargetInfo.Id}.");
}
if (colorInfo.AdvancedColorEnabled)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is enabled for display {path.TargetInfo.Id}.");
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is NOT enabled for display {path.TargetInfo.Id}.");
}
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get advanced color settings for display {path.TargetInfo.Id}.");
}
// get SDR white levels
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get SDR white levels for adapter {path.TargetInfo.AdapterId.Value}.");
var whiteLevelInfo = new DISPLAYCONFIG_SDR_WHITE_LEVEL();
whiteLevelInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
whiteLevelInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SDR_WHITE_LEVEL>();
whiteLevelInfo.Header.AdapterId = path.TargetInfo.AdapterId;
whiteLevelInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref whiteLevelInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found SDR White levels for display {path.TargetInfo.Id}.");
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get SDR White levels for display {path.TargetInfo.Id}.");
}
hdrInfos[hdrInfoCount] = new ADVANCED_HDR_INFO_PER_PATH();
hdrInfos[hdrInfoCount].AdapterId = path.TargetInfo.AdapterId;
hdrInfos[hdrInfoCount].Id = path.TargetInfo.Id;
hdrInfos[hdrInfoCount].AdvancedColorInfo = colorInfo;
hdrInfos[hdrInfoCount].SDRWhiteLevel = whiteLevelInfo;
hdrInfoCount++;
}
// Store the active paths and modes in our display config object
windowsDisplayConfig.DisplayConfigPaths = paths;
windowsDisplayConfig.DisplayConfigModes = modes;
windowsDisplayConfig.DisplayHDRStates = hdrInfos;
windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers();
return windowsDisplayConfig;
}
private LUID AdapterValueToLUID(ulong adapterValue)
{
LUID luid = new LUID();
luid.LowPart = (uint)(adapterValue & uint.MaxValue);
luid.HighPart = (uint)(adapterValue >> 32);
return luid;
}
public string PrintActiveConfig()
{
string stringToReturn = "";
stringToReturn += $"****** WINDOWS CCD CONFIGURATION *******\n";
// Get the size of the largest Active Paths and Modes arrays
int pathCount = 0;
int modeCount = 0;
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
}
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Getting the current Display Config path and mode arrays");
var paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
var modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Warn($"WinLibrary/PrintActiveConfig: The displays were modified between GetDisplayConfigBufferSizes and QueryDisplayConfig so we need to get the buffer sizes again.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Getting the size of the largest Active Paths and Modes arrays");
// Screen changed in between GetDisplayConfigBufferSizes and QueryDisplayConfig, so we need to get buffer sizes again
// as per https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-querydisplayconfig
err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
}
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Getting the current Display Config path and mode arrays");
paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: ERROR - The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
throw new WinLibraryException($"The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again.");
}
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays.");
}
foreach (var path in paths)
{
stringToReturn += $"----++++==== Path ====++++----\n";
// get display source name
var sourceInfo = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
sourceInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
sourceInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
sourceInfo.Header.AdapterId = path.SourceInfo.AdapterId;
sourceInfo.Header.Id = path.SourceInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref sourceInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {path.SourceInfo.Id}.");
stringToReturn += $"****** Interrogating Display Source {path.SourceInfo.Id} *******\n";
stringToReturn += $"Found Display Source {sourceInfo.ViewGdiDeviceName}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/PrintActiveConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the source info for source adapter #{path.SourceInfo.AdapterId}");
}
// get display target name
var targetInfo = new DISPLAYCONFIG_TARGET_DEVICE_NAME();
targetInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
targetInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_TARGET_DEVICE_NAME>();
targetInfo.Header.AdapterId = path.TargetInfo.AdapterId;
targetInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref targetInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Connector Instance: {targetInfo.ConnectorInstance} for source {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: EDID Manufacturer ID: {targetInfo.EdidManufactureId} for source {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: EDID Product Code ID: {targetInfo.EdidProductCodeId} for source {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Flags Friendly Name from EDID: {targetInfo.Flags.FriendlyNameFromEdid} for source {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Flags Friendly Name Forced: {targetInfo.Flags.FriendlyNameForced} for source {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Flags EDID ID is Valid: {targetInfo.Flags.EdidIdsValid} for source {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Monitor Device Path: {targetInfo.MonitorDevicePath} for source {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Monitor Friendly Device Name: {targetInfo.MonitorFriendlyDeviceName} for source {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Output Technology: {targetInfo.OutputTechnology} for source {path.TargetInfo.Id}.");
stringToReturn += $"****** Interrogating Display Target {targetInfo.MonitorFriendlyDeviceName} *******\n";
stringToReturn += $" Connector Instance: {targetInfo.ConnectorInstance}\n";
stringToReturn += $" EDID Manufacturer ID: {targetInfo.EdidManufactureId}\n";
stringToReturn += $" EDID Product Code ID: {targetInfo.EdidProductCodeId}\n";
stringToReturn += $" Flags Friendly Name from EDID: {targetInfo.Flags.FriendlyNameFromEdid}\n";
stringToReturn += $" Flags Friendly Name Forced: {targetInfo.Flags.FriendlyNameForced}\n";
stringToReturn += $" Flags EDID ID is Valid: {targetInfo.Flags.EdidIdsValid}\n";
stringToReturn += $" Monitor Device Path: {targetInfo.MonitorDevicePath}\n";
stringToReturn += $" Monitor Friendly Device Name: {targetInfo.MonitorFriendlyDeviceName}\n";
stringToReturn += $" Output Technology: {targetInfo.OutputTechnology}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/PrintActiveConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{path.TargetInfo.Id}");
}
// get display adapter name
var adapterInfo = new DISPLAYCONFIG_ADAPTER_NAME();
adapterInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME;
adapterInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_ADAPTER_NAME>();
adapterInfo.Header.AdapterId = path.TargetInfo.AdapterId;
adapterInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref adapterInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Found Adapter Device Path {adapterInfo.AdapterDevicePath} for source {path.TargetInfo.AdapterId}.");
stringToReturn += $"****** Interrogating Display Adapter {adapterInfo.AdapterDevicePath} *******\n";
stringToReturn += $" Display Adapter {adapterInfo.AdapterDevicePath}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the adapter device path for target #{path.TargetInfo.AdapterId}");
}
// get display target preferred mode
var targetPreferredInfo = new DISPLAYCONFIG_TARGET_PREFERRED_MODE();
targetPreferredInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE;
targetPreferredInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_TARGET_PREFERRED_MODE>();
targetPreferredInfo.Header.AdapterId = path.TargetInfo.AdapterId;
targetPreferredInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref targetPreferredInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Preferred Width {targetPreferredInfo.Width} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Preferred Height {targetPreferredInfo.Height} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Video Signal Info Active Size: ({targetPreferredInfo.TargetMode.TargetVideoSignalInfo.ActiveSize.Cx}x{targetPreferredInfo.TargetMode.TargetVideoSignalInfo.ActiveSize.Cy} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Video Signal Info Total Size: ({targetPreferredInfo.TargetMode.TargetVideoSignalInfo.TotalSize.Cx}x{targetPreferredInfo.TargetMode.TargetVideoSignalInfo.TotalSize.Cy} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Video Signal Info HSync Frequency: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.HSyncFreq} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Video Signal Info VSync Frequency: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.VSyncFreq} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Video Signal Info Pixel Rate: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.PixelRate} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Video Signal Info Scan Line Ordering: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.ScanLineOrdering} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Target Video Signal Info Video Standard: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.VideoStandard} for target {path.TargetInfo.Id}.");
stringToReturn += $"****** Interrogating Target Preferred Mode for Display {path.TargetInfo.Id} *******\n";
stringToReturn += $" Target Preferred Width {targetPreferredInfo.Width} for target {path.TargetInfo.Id}\n";
stringToReturn += $" Target Preferred Height {targetPreferredInfo.Height} for target {path.TargetInfo.Id}\n";
stringToReturn += $" Target Video Signal Info Active Size: ({targetPreferredInfo.TargetMode.TargetVideoSignalInfo.ActiveSize.Cx}x{targetPreferredInfo.TargetMode.TargetVideoSignalInfo.ActiveSize.Cy}\n";
stringToReturn += $" Target Video Signal Info Total Size: ({targetPreferredInfo.TargetMode.TargetVideoSignalInfo.TotalSize.Cx}x{targetPreferredInfo.TargetMode.TargetVideoSignalInfo.TotalSize.Cy}\n";
stringToReturn += $" Target Video Signal Info HSync Frequency: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.HSyncFreq}\n";
stringToReturn += $" Target Video Signal Info VSync Frequency: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.VSyncFreq}\n";
stringToReturn += $" Target Video Signal Info Pixel Rate: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.PixelRate}\n";
stringToReturn += $" Target Video Signal Info Scan Line Ordering: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.ScanLineOrdering}\n";
stringToReturn += $" Target Video Signal Info Video Standard: {targetPreferredInfo.TargetMode.TargetVideoSignalInfo.VideoStandard}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the preferred target name for display #{path.TargetInfo.Id}");
}
// get display target base type
var targetBaseTypeInfo = new DISPLAYCONFIG_TARGET_BASE_TYPE();
targetBaseTypeInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE;
targetBaseTypeInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_TARGET_BASE_TYPE>();
targetBaseTypeInfo.Header.AdapterId = path.TargetInfo.AdapterId;
targetBaseTypeInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref targetBaseTypeInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Virtual Resolution is Disabled: {targetBaseTypeInfo.BaseOutputTechnology} for target {path.TargetInfo.Id}.");
stringToReturn += $"****** Interrogating Target Base Type for Display {path.TargetInfo.Id} *******\n";
stringToReturn += $" Base Output Technology: {targetBaseTypeInfo.BaseOutputTechnology}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target base type for display #{path.TargetInfo.Id}");
}
// get display support virtual resolution
var supportVirtResInfo = new DISPLAYCONFIG_SUPPORT_VIRTUAL_RESOLUTION();
supportVirtResInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION;
supportVirtResInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SUPPORT_VIRTUAL_RESOLUTION>();
supportVirtResInfo.Header.AdapterId = path.TargetInfo.AdapterId;
supportVirtResInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref supportVirtResInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Base Output Technology: {supportVirtResInfo.IsMonitorVirtualResolutionDisabled} for target {path.TargetInfo.Id}.");
stringToReturn += $"****** Interrogating Target Supporting virtual resolution for Display {path.TargetInfo.Id} *******\n";
stringToReturn += $" Virtual Resolution is Disabled: {supportVirtResInfo.IsMonitorVirtualResolutionDisabled}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to find out the virtual resolution support for display #{path.TargetInfo.Id}");
}
//get advanced color info
var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
colorInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
colorInfo.Header.AdapterId = path.TargetInfo.AdapterId;
colorInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref colorInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Advanced Color Supported: {colorInfo.AdvancedColorSupported} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Advanced Color Enabled: {colorInfo.AdvancedColorEnabled} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Advanced Color Force Disabled: {colorInfo.AdvancedColorForceDisabled} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Bits per Color Channel: {colorInfo.BitsPerColorChannel} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Color Encoding: {colorInfo.ColorEncoding} for target {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Wide Color Enforced: {colorInfo.WideColorEnforced} for target {path.TargetInfo.Id}.");
stringToReturn += $"****** Interrogating Advanced Color Info for Display {path.TargetInfo.Id} *******\n";
stringToReturn += $" Advanced Color Supported: {colorInfo.AdvancedColorSupported}\n";
stringToReturn += $" Advanced Color Enabled: {colorInfo.AdvancedColorEnabled}\n";
stringToReturn += $" Advanced Color Force Disabled: {colorInfo.AdvancedColorForceDisabled}\n";
stringToReturn += $" Bits per Color Channel: {colorInfo.BitsPerColorChannel}\n";
stringToReturn += $" Color Encoding: {colorInfo.ColorEncoding}\n";
stringToReturn += $" Wide Color Enforced: {colorInfo.WideColorEnforced}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to find out the virtual resolution support for display #{path.TargetInfo.Id}");
}
// get SDR white levels
var whiteLevelInfo = new DISPLAYCONFIG_SDR_WHITE_LEVEL();
whiteLevelInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
whiteLevelInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SDR_WHITE_LEVEL>();
whiteLevelInfo.Header.AdapterId = path.TargetInfo.AdapterId;
whiteLevelInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref whiteLevelInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found SDR White Level: {whiteLevelInfo.SDRWhiteLevel} for target {path.TargetInfo.Id}.");
stringToReturn += $"****** Interrogating SDR Whilte Level for Display {path.TargetInfo.Id} *******\n";
stringToReturn += $" SDR White Level: {whiteLevelInfo.SDRWhiteLevel}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to find out the SDL white level for display #{path.TargetInfo.Id}");
}
}
return stringToReturn;
}
public bool SetActiveConfig(WINDOWS_DISPLAY_CONFIG displayConfig)
{
// Get the all possible windows display configs
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Generating a list of all the current display configs");
WINDOWS_DISPLAY_CONFIG allWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ALL_PATHS);
// Now we go through the Paths to update the LUIDs as per Soroush's suggestion
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Patching the adapter IDs to make the saved config valid");
PatchAdapterIDs(ref displayConfig, allWindowsDisplayConfig.DisplayAdapters);
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Testing whether the display configuration is valid");
// Test whether a specified display configuration is supported on the computer
uint myPathsCount = (uint)displayConfig.DisplayConfigPaths.Length;
uint myModesCount = (uint)displayConfig.DisplayConfigModes.Length;
WIN32STATUS err = CCDImport.SetDisplayConfig(myPathsCount, displayConfig.DisplayConfigPaths, myModesCount, displayConfig.DisplayConfigModes, SDC.DISPLAYMAGICIAN_VALIDATE);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Successfully validated that the display configuration supplied would work!");
}
else
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: ERROR - SetDisplayConfig couldn't validate the display configuration supplied. This display configuration wouldn't work.");
return false;
}
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Yay! The display configuration is valid! Attempting to set the Display Config now");
// Now set the specified display configuration for this computer
err = CCDImport.SetDisplayConfig(myPathsCount, displayConfig.DisplayConfigPaths, myModesCount, displayConfig.DisplayConfigModes, SDC.DISPLAYMAGICIAN_SET);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Successfully set the display configuration to the settings supplied!");
}
else
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: ERROR - SetDisplayConfig couldn't set the display configuration using the settings supplied. Something is wrong.");
throw new WinLibraryException($"SetDisplayConfig couldn't set the display configuration using the settings supplied. Something is wrong.");
}
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: SUCCESS! The display configuration has been successfully applied");
foreach (ADVANCED_HDR_INFO_PER_PATH myHDRstate in displayConfig.DisplayHDRStates)
{
SharedLogger.logger.Trace($"Trying to get information whether HDR color is in use now on Display {myHDRstate.Id}.");
// Get advanced HDR info
var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
colorInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
colorInfo.Header.AdapterId = myHDRstate.AdapterId;
colorInfo.Header.Id = myHDRstate.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref colorInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Advanced Color Info gathered from Display {myHDRstate.Id}");
if (myHDRstate.AdvancedColorInfo.AdvancedColorSupported && colorInfo.AdvancedColorEnabled != myHDRstate.AdvancedColorInfo.AdvancedColorEnabled)
{
SharedLogger.logger.Trace($"HDR is available for use on Display {myHDRstate.Id}, and we want it set to {myHDRstate.AdvancedColorInfo.AdvancedColorEnabled} but is currently {colorInfo.AdvancedColorEnabled}.");
var setColorState = new DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE();
setColorState.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
setColorState.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE>();
setColorState.Header.AdapterId = myHDRstate.AdapterId;
setColorState.Header.Id = myHDRstate.Id;
setColorState.EnableAdvancedColor = myHDRstate.AdvancedColorInfo.AdvancedColorEnabled;
err = CCDImport.DisplayConfigSetDeviceInfo(ref setColorState);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: SUCCESS! Set HDR successfully to {myHDRstate.AdvancedColorInfo.AdvancedColorEnabled} on Display {myHDRstate.Id}");
}
else
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: ERROR - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to set the HDR settings for display #{myHDRstate.Id}");
return false;
}
}
else
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Skipping setting HDR on Display {myHDRstate.Id} as it does not support HDR");
}
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to find out if HDR is supported for display #{myHDRstate.Id}");
}
}
return true;
}
public bool IsActiveConfig(WINDOWS_DISPLAY_CONFIG displayConfig)
{
// Get the current windows display configs to compare to the one we loaded
WINDOWS_DISPLAY_CONFIG currentWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS);
// Check whether the display config is in use now
SharedLogger.logger.Trace($"WinLibrary/IsActiveConfig: Checking whether the display configuration is already being used.");
if (displayConfig.Equals(currentWindowsDisplayConfig))
{
SharedLogger.logger.Trace($"WinLibrary/IsActiveConfig: The display configuration is already being used (supplied displayConfig Equals currentWindowsDisplayConfig");
return true;
}
else
{
SharedLogger.logger.Trace($"WinLibrary/IsActiveConfig: The display configuration is NOT currently in use (supplied displayConfig Equals currentWindowsDisplayConfig");
return false;
}
}
public bool IsValidConfig(WINDOWS_DISPLAY_CONFIG displayConfig)
{
// Get the current windows display configs
WINDOWS_DISPLAY_CONFIG allWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ALL_PATHS);
SharedLogger.logger.Trace("WinLibrary/PatchAdapterIDs: Going through the list of adapters we stored in the config to make sure they still exist");
// Firstly check that the Adapter Names are still currently available (i.e. the adapter hasn't been replaced).
foreach (string savedAdapterName in displayConfig.DisplayAdapters.Values)
{
// If there is even one of the saved adapters that has changed, then it's no longer possible
// to use this display config!
if (!allWindowsDisplayConfig.DisplayAdapters.Values.Contains(savedAdapterName))
{
SharedLogger.logger.Error($"WinLibrary/PatchAdapterIDs: ERROR - Saved adapter {savedAdapterName} is not available right now! This display configuration won't work!");
return false;
}
}
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: All teh adapters that the display configuration uses are still avilable to use now!");
// Now we go through the Paths to update the LUIDs as per Soroush's suggestion
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Attemptong to patch the saved display configuration's adapter IDs so that it will still work (these change at each boot)");
PatchAdapterIDs(ref displayConfig, allWindowsDisplayConfig.DisplayAdapters);
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Testing whether the display configuration is valid ");
// Test whether a specified display configuration is supported on the computer
uint myPathsCount = (uint)displayConfig.DisplayConfigPaths.Length;
uint myModesCount = (uint)displayConfig.DisplayConfigModes.Length;
WIN32STATUS err = CCDImport.SetDisplayConfig(myPathsCount, displayConfig.DisplayConfigPaths, myModesCount, displayConfig.DisplayConfigModes, SDC.DISPLAYMAGICIAN_VALIDATE);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: SetDisplayConfig validated that the display configuration is valid and can be used!");
return true;
}
else
{
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: SetDisplayConfig confirmed that the display configuration is invalid and cannot be used!");
return false;
}
}
public bool IsPossibleConfig(WINDOWS_DISPLAY_CONFIG displayConfig)
{
// We want to check the Windows Display profile can be used now
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Testing whether the Windows display configuration is possible to be used now");
// check what the currently available displays are (include the ones not active)
List<string> currentAllIds = GetAllConnectedDisplayIdentifiers();
// CHeck that we have all the displayConfig DisplayIdentifiers we need available now
//if (currentAllIds.Intersect(displayConfig.DisplayIdentifiers).Count() == displayConfig.DisplayIdentifiers.Count)
if (displayConfig.DisplayIdentifiers.All(value => currentAllIds.Contains(value)))
{
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Success! THe Windows display configuration is possible to be used now");
return true;
}
else
{
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Uh oh! THe Windows display configuration is possible cannot be used now");
return false;
}
}
public List<string> GetCurrentDisplayIdentifiers()
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentDisplayIdentifiers: Getting the current display identifiers for the displays in use now");
return GetSomeDisplayIdentifiers(QDC.QDC_ONLY_ACTIVE_PATHS);
}
public List<string> GetAllConnectedDisplayIdentifiers()
{
SharedLogger.logger.Error($"WinLibrary/GetAllConnectedDisplayIdentifiers: Getting all the display identifiers that can possibly be used");
return GetSomeDisplayIdentifiers(QDC.QDC_ALL_PATHS);
}
private List<string> GetSomeDisplayIdentifiers(QDC selector = QDC.QDC_ONLY_ACTIVE_PATHS)
{
SharedLogger.logger.Debug($"WinLibrary/GetCurrentDisplayIdentifiers: Generating the unique Display Identifiers for the currently active configuration");
List<string> displayIdentifiers = new List<string>();
SharedLogger.logger.Trace($"WinLibrary/GetCurrentDisplayIdentifiers: Testing whether the display configuration is valid (allowing tweaks).");
// Get the size of the largest Active Paths and Modes arrays
int pathCount = 0;
int modeCount = 0;
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(selector, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
}
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Getting the current Display Config path and mode arrays");
var paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
var modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(selector, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Warn($"WinLibrary/GetSomeDisplayIdentifiers: The displays were modified between GetDisplayConfigBufferSizes and QueryDisplayConfig so we need to get the buffer sizes again.");
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Getting the size of the largest Active Paths and Modes arrays");
// Screen changed in between GetDisplayConfigBufferSizes and QueryDisplayConfig, so we need to get buffer sizes again
// as per https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-querydisplayconfig
err = CCDImport.GetDisplayConfigBufferSizes(selector, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetSomeDisplayIdentifiers: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
}
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Getting the current Display Config path and mode arrays");
paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(selector, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Error($"WinLibrary/GetSomeDisplayIdentifiers: ERROR - The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
throw new WinLibraryException($"The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetSomeDisplayIdentifiers: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again.");
}
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetSomeDisplayIdentifiers: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays.");
}
foreach (var path in paths)
{
if (path.TargetInfo.TargetAvailable == false)
{
// We want to skip this one cause it's not valid
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Skipping path due to TargetAvailable not existing in display #{path.TargetInfo.Id}");
continue;
}
// get display source name
var sourceInfo = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
sourceInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
sourceInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
sourceInfo.Header.AdapterId = path.SourceInfo.AdapterId;
sourceInfo.Header.Id = path.SourceInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref sourceInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Successfully got the source info from {path.SourceInfo.Id}.");
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetSomeDisplayIdentifiers: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{path.SourceInfo.Id}");
}
// get display target name
var targetInfo = new DISPLAYCONFIG_TARGET_DEVICE_NAME();
targetInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
targetInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_TARGET_DEVICE_NAME>();
targetInfo.Header.AdapterId = path.TargetInfo.AdapterId;
targetInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref targetInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Successfully got the target info from {path.TargetInfo.Id}.");
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetSomeDisplayIdentifiers: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{path.TargetInfo.Id}");
}
// get display adapter name
var adapterInfo = new DISPLAYCONFIG_ADAPTER_NAME();
adapterInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME;
adapterInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_ADAPTER_NAME>();
adapterInfo.Header.AdapterId = path.TargetInfo.AdapterId;
adapterInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref adapterInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Successfully got the display name info from {path.TargetInfo.Id}.");
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetSomeDisplayIdentifiers: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{path.TargetInfo.Id}");
}
// Create an array of all the important display info we need to record
List<string> displayInfo = new List<string>();
displayInfo.Add("WINAPI");
try
{
displayInfo.Add(adapterInfo.AdapterDevicePath.ToString());
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"WinLibrary/GetSomeDisplayIdentifiers: Exception getting Windows Display Adapter Device Path from video card. Substituting with a # instead");
displayInfo.Add("#");
}
try
{
displayInfo.Add(targetInfo.OutputTechnology.ToString());
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"WinLibrary/GetSomeDisplayIdentifiers: Exception getting Windows Display Connector Instance from video card. Substituting with a # instead");
displayInfo.Add("#");
}
try
{
displayInfo.Add(targetInfo.EdidManufactureId.ToString());
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"WinLibrary/GetSomeDisplayIdentifiers: Exception getting Windows Display EDID Manufacturer Code from video card. Substituting with a # instead");
displayInfo.Add("#");
}
try
{
displayInfo.Add(targetInfo.EdidProductCodeId.ToString());
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"WinLibrary/GetSomeDisplayIdentifiers: Exception getting Windows Display EDID Product Code from video card. Substituting with a # instead");
displayInfo.Add("#");
}
try
{
displayInfo.Add(targetInfo.MonitorFriendlyDeviceName.ToString());
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"WinLibrary/GetSomeDisplayIdentifiers: Exception getting Windows Display Target Friendly name from video card. Substituting with a # instead");
displayInfo.Add("#");
}
// Create a display identifier out of it
string displayIdentifier = String.Join("|", displayInfo);
// Add it to the list of display identifiers so we can return it
// but only add it if it doesn't already exist. Otherwise we get duplicates :/
if (!displayIdentifiers.Contains(displayIdentifier))
{
displayIdentifiers.Add(displayIdentifier);
SharedLogger.logger.Debug($"ProfileRepository/GenerateProfileDisplayIdentifiers: DisplayIdentifier: {displayIdentifier}");
}
}
// Sort the display identifiers
displayIdentifiers.Sort();
return displayIdentifiers;
}
public List<string> GetCurrentPCIVideoCardVendors()
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentPCIVideoCardVendors: Getting the current PCI vendor ids for the videocards reported to Windows");
List<string> videoCardVendorIds = new List<string>();
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: Testing whether the display configuration is valid (allowing tweaks).");
// Get the size of the largest Active Paths and Modes arrays
int pathCount = 0;
int modeCount = 0;
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentPCIVideoCardVendors: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
}
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Getting the current Display Config path and mode arrays");
var paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
var modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Warn($"WinLibrary/GetCurrentPCIVideoCardVendors: The displays were modified between GetDisplayConfigBufferSizes and QueryDisplayConfig so we need to get the buffer sizes again.");
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: Getting the size of the largest Active Paths and Modes arrays");
// Screen changed in between GetDisplayConfigBufferSizes and QueryDisplayConfig, so we need to get buffer sizes again
// as per https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-querydisplayconfig
err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentPCIVideoCardVendors: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
}
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Getting the current Display Config path and mode arrays");
paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentPCIVideoCardVendors: ERROR - The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
throw new WinLibraryException($"The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentPCIVideoCardVendors: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again.");
}
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentPCIVideoCardVendors: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays.");
}
foreach (var path in paths)
{
if (path.TargetInfo.TargetAvailable == false)
{
// We want to skip this one cause it's not valid
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: Skipping path due to TargetAvailable not existing in display #{path.TargetInfo.Id}");
continue;
}
// get display adapter name
var adapterInfo = new DISPLAYCONFIG_ADAPTER_NAME();
adapterInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME;
adapterInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_ADAPTER_NAME>();
adapterInfo.Header.AdapterId = path.TargetInfo.AdapterId;
adapterInfo.Header.Id = path.TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref adapterInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: Successfully got the display name info from {path.TargetInfo.Id}.");
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetCurrentPCIVideoCardVendors: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{path.TargetInfo.Id}");
}
try
{
// The AdapterDevicePath is something like "\\\\?\\PCI#VEN_10DE&DEV_2482&SUBSYS_408E1458&REV_A1#4&2283f625&0&0019#{5b45201d-f2f2-4f3b-85bb-30ff1f953599}"
// We only want the vendor ID
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: The AdapterDevicePath for this path is :{adapterInfo.AdapterDevicePath}");
// Match against the vendor ID
string pattern = @"VEN_([\d\w]{4})&";
Match match = Regex.Match(adapterInfo.AdapterDevicePath, pattern);
if (match.Success)
{
string VendorId = match.Groups[1].Value;
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: The matched PCI Vendor ID is :{VendorId }");
if (!videoCardVendorIds.Contains(VendorId))
{
videoCardVendorIds.Add(VendorId);
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: Stored PCI vendor ID {VendorId} as we haven't already got it");
}
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: The PCI Vendor ID pattern wasn't matched so we didn't record a vendor ID.");
}
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"WinLibrary/GetCurrentPCIVideoCardVendors: Exception getting PCI Vendor ID from Display Adapter {path.SourceInfo.AdapterId}.");
}
}
return videoCardVendorIds;
}
}
[global::System.Serializable]
public class WinLibraryException : Exception
{
public WinLibraryException() { }
public WinLibraryException(string message) : base(message) { }
public WinLibraryException(string message, Exception inner) : base(message, inner) { }
protected WinLibraryException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}