2025-10-20 16:22:47 +08:00
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
using System.Collections.Generic ;
2025-11-27 18:14:14 +08:00
using System.Globalization ;
2025-10-20 16:22:47 +08:00
using System.Linq ;
using System.Threading ;
using System.Threading.Tasks ;
using ManagedCommon ;
2025-11-24 18:08:11 +08:00
using PowerDisplay.Common.Interfaces ;
using PowerDisplay.Common.Models ;
2025-10-20 16:22:47 +08:00
using WmiLight ;
2025-11-24 18:08:11 +08:00
using Monitor = PowerDisplay . Common . Models . Monitor ;
2025-10-20 16:22:47 +08:00
2025-11-24 21:58:34 +08:00
namespace PowerDisplay.Common.Drivers.WMI
2025-10-20 16:22:47 +08:00
{
/// <summary>
/// WMI monitor controller for controlling internal laptop displays.
/// Rewritten to use WmiLight library for Native AOT compatibility.
/// </summary>
public partial class WmiController : IMonitorController , IDisposable
{
private const string WmiNamespace = @"root\WMI" ;
private const string BrightnessQueryClass = "WmiMonitorBrightness" ;
private const string BrightnessMethodClass = "WmiMonitorBrightnessMethods" ;
private const string MonitorIdClass = "WmiMonitorID" ;
Add GPO rule and profile management for PowerDisplay
Introduced a new GPO rule to manage PowerDisplay's enabled state via Group Policy, including updates to `GPOWrapper` and policy files (`PowerToys.admx` and `PowerToys.adml`).
Enhanced the PowerDisplay UI with profile management features, including quick apply, add, edit, and delete functionality. Updated `MainWindow.xaml` and `PowerDisplayPage.xaml` to support these changes, and added localized strings for improved accessibility.
Refactored `MainViewModel` to include a `Profiles` collection, `HasProfiles` property, and `ApplyProfileCommand`. Added methods to load profiles from disk and signal updates to PowerDisplay.
Improved error handling in `DdcCiController` and `WmiController` with input validation and WMI error classification. Optimized handle cleanup in `PhysicalMonitorHandleManager` with a more efficient algorithm.
Refactored `dllmain.cpp` to prevent duplicate PowerDisplay process launches. Updated initialization logic in `MainWindow.xaml.cs` to ensure proper ViewModel setup.
Localized strings for tooltips, warnings, and dialogs. Improved async behavior, logging, and UI accessibility.
2025-12-01 04:32:29 +08:00
// Common WMI error codes for classification
private const int WbemENotFound = unchecked ( ( int ) 0x80041002 ) ;
private const int WbemEAccessDenied = unchecked ( ( int ) 0x80041003 ) ;
private const int WbemEProviderFailure = unchecked ( ( int ) 0x80041004 ) ;
private const int WbemEInvalidQuery = unchecked ( ( int ) 0x80041017 ) ;
private const int WmiFeatureNotSupported = 0x1068 ;
2025-10-20 16:22:47 +08:00
private bool _disposed ;
Add GPO rule and profile management for PowerDisplay
Introduced a new GPO rule to manage PowerDisplay's enabled state via Group Policy, including updates to `GPOWrapper` and policy files (`PowerToys.admx` and `PowerToys.adml`).
Enhanced the PowerDisplay UI with profile management features, including quick apply, add, edit, and delete functionality. Updated `MainWindow.xaml` and `PowerDisplayPage.xaml` to support these changes, and added localized strings for improved accessibility.
Refactored `MainViewModel` to include a `Profiles` collection, `HasProfiles` property, and `ApplyProfileCommand`. Added methods to load profiles from disk and signal updates to PowerDisplay.
Improved error handling in `DdcCiController` and `WmiController` with input validation and WMI error classification. Optimized handle cleanup in `PhysicalMonitorHandleManager` with a more efficient algorithm.
Refactored `dllmain.cpp` to prevent duplicate PowerDisplay process launches. Updated initialization logic in `MainWindow.xaml.cs` to ensure proper ViewModel setup.
Localized strings for tooltips, warnings, and dialogs. Improved async behavior, logging, and UI accessibility.
2025-12-01 04:32:29 +08:00
/// <summary>
/// Classifies WMI exceptions into user-friendly error messages.
/// </summary>
private static MonitorOperationResult ClassifyWmiError ( WmiException ex , string operation )
{
var hresult = ex . HResult ;
return hresult switch
{
WbemENotFound = > MonitorOperationResult . Failure ( $"WMI class not found during {operation}. This feature may not be supported on your system." , hresult ) ,
WbemEAccessDenied = > MonitorOperationResult . Failure ( $"Access denied during {operation}. Administrator privileges may be required." , hresult ) ,
WbemEProviderFailure = > MonitorOperationResult . Failure ( $"WMI provider failure during {operation}. The display driver may not support this feature." , hresult ) ,
WbemEInvalidQuery = > MonitorOperationResult . Failure ( $"Invalid WMI query during {operation}. This is likely a bug." , hresult ) ,
WmiFeatureNotSupported = > MonitorOperationResult . Failure ( $"WMI brightness control not supported on this system during {operation}." , hresult ) ,
_ = > MonitorOperationResult . Failure ( $"WMI error during {operation}: {ex.Message}" , hresult ) ,
} ;
}
/// <summary>
/// Determines if the WMI error is expected for systems without WMI brightness support.
/// </summary>
private static bool IsExpectedUnsupportedError ( WmiException ex )
{
return ex . HResult = = WmiFeatureNotSupported | | ex . HResult = = WbemENotFound ;
}
2025-12-03 06:24:20 +08:00
/// <summary>
/// Escape special characters in WMI query strings.
/// WMI requires backslashes and single quotes to be escaped in WHERE clauses.
/// See: https://learn.microsoft.com/en-us/windows/win32/wmisdk/wql-sql-for-wmi
/// </summary>
/// <param name="value">The string value to escape.</param>
/// <returns>The escaped string safe for use in WMI queries.</returns>
private static string EscapeWmiString ( string value )
{
if ( string . IsNullOrEmpty ( value ) )
{
return value ;
}
// WMI requires backslashes and single quotes to be escaped in WHERE clauses
// Backslash must be escaped first to avoid double-escaping the quote's backslash
return value . Replace ( "\\" , "\\\\" ) . Replace ( "'" , "\\'" ) ;
}
/// <summary>
/// Extract hardware ID from WMI InstanceName.
/// InstanceName format: "DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0"
/// Returns the second segment (e.g., "BOE0900") which is the manufacturer+product code.
/// </summary>
/// <param name="instanceName">The WMI InstanceName.</param>
/// <returns>The hardware ID extracted from the InstanceName, or empty string if extraction fails.</returns>
private static string ExtractHardwareIdFromInstanceName ( string instanceName )
{
if ( string . IsNullOrEmpty ( instanceName ) )
{
return string . Empty ;
}
// Split by backslash: ["DISPLAY", "BOE0900", "4&10fd3ab1&0&UID265988_0"]
var parts = instanceName . Split ( '\\' ) ;
if ( parts . Length > = 2 & & ! string . IsNullOrEmpty ( parts [ 1 ] ) )
{
// Return the second part (e.g., "BOE0900")
return parts [ 1 ] ;
}
return string . Empty ;
}
2025-12-04 19:12:49 +08:00
/// <summary>
/// Get monitor number from MonitorDisplayInfo dictionary by matching HardwareId.
/// Uses QueryDisplayConfig path index which matches Windows Display Settings "Identify" feature.
/// </summary>
/// <param name="hardwareId">The hardware ID to match (e.g., "LEN4038", "BOE0900").</param>
/// <param name="monitorDisplayInfos">Dictionary of monitor display info from QueryDisplayConfig.</param>
/// <returns>Monitor number (1-based) or 0 if not found.</returns>
private static int GetMonitorNumberFromDisplayInfo ( string hardwareId , Dictionary < string , Drivers . DDC . MonitorDisplayInfo > monitorDisplayInfos )
{
if ( string . IsNullOrEmpty ( hardwareId ) | | monitorDisplayInfos = = null | | monitorDisplayInfos . Count = = 0 )
{
return 0 ;
}
foreach ( var kvp in monitorDisplayInfos )
{
if ( ! string . IsNullOrEmpty ( kvp . Value . HardwareId ) & &
kvp . Value . HardwareId . Equals ( hardwareId , StringComparison . OrdinalIgnoreCase ) )
{
Logger . LogDebug ( $"WMI: Matched HardwareId '{hardwareId}' to MonitorNumber {kvp.Value.MonitorNumber}" ) ;
return kvp . Value . MonitorNumber ;
}
}
Logger . LogWarning ( $"WMI: Could not find MonitorNumber for HardwareId '{hardwareId}'" ) ;
return 0 ;
}
2025-10-20 16:22:47 +08:00
public string Name = > "WMI Monitor Controller (WmiLight)" ;
/// <summary>
2025-12-03 06:24:20 +08:00
/// Check if the specified monitor can be controlled.
/// Verifies the specific monitor exists in WMI by filtering on InstanceName.
2025-10-20 16:22:47 +08:00
/// </summary>
public async Task < bool > CanControlMonitorAsync ( Monitor monitor , CancellationToken cancellationToken = default )
{
Add GPO rule and profile management for PowerDisplay
Introduced a new GPO rule to manage PowerDisplay's enabled state via Group Policy, including updates to `GPOWrapper` and policy files (`PowerToys.admx` and `PowerToys.adml`).
Enhanced the PowerDisplay UI with profile management features, including quick apply, add, edit, and delete functionality. Updated `MainWindow.xaml` and `PowerDisplayPage.xaml` to support these changes, and added localized strings for improved accessibility.
Refactored `MainViewModel` to include a `Profiles` collection, `HasProfiles` property, and `ApplyProfileCommand`. Added methods to load profiles from disk and signal updates to PowerDisplay.
Improved error handling in `DdcCiController` and `WmiController` with input validation and WMI error classification. Optimized handle cleanup in `PhysicalMonitorHandleManager` with a more efficient algorithm.
Refactored `dllmain.cpp` to prevent duplicate PowerDisplay process launches. Updated initialization logic in `MainWindow.xaml.cs` to ensure proper ViewModel setup.
Localized strings for tooltips, warnings, and dialogs. Improved async behavior, logging, and UI accessibility.
2025-12-01 04:32:29 +08:00
ArgumentNullException . ThrowIfNull ( monitor ) ;
Refactor PowerDisplay for dynamic monitor capabilities
Removed reliance on static `MonitorType` enumeration, replacing it with dynamic `CommunicationMethod` for better flexibility. Updated `IMonitorController` and `MonitorManager` to dynamically determine monitor control capabilities.
Refactored `Monitor` model to streamline properties and improve color temperature handling. Enhanced `MonitorViewModel` with unified methods for brightness, contrast, volume, and color temperature updates, improving UI responsiveness and hardware synchronization.
Improved settings handling by adding support for hidden monitors, preserving user preferences, and separating UI configuration from hardware parameter updates. Updated the PowerDisplay Settings UI with warnings, confirmation dialogs, and better VCP capabilities formatting.
Removed legacy IPC code in favor of event-driven settings updates. Conducted general code cleanup, improving logging, error handling, and documentation for maintainability.
2025-11-14 16:45:22 +08:00
if ( monitor . CommunicationMethod ! = "WMI" )
2025-10-20 16:22:47 +08:00
{
return false ;
}
2025-12-03 06:24:20 +08:00
// If no InstanceName, we can't verify the specific monitor
if ( string . IsNullOrEmpty ( monitor . InstanceName ) )
{
return false ;
}
2025-10-20 16:22:47 +08:00
return await Task . Run (
( ) = >
{
try
{
using var connection = new WmiConnection ( WmiNamespace ) ;
2025-12-03 06:24:20 +08:00
// Filter by InstanceName to verify this specific monitor exists
var escapedInstanceName = EscapeWmiString ( monitor . InstanceName ) ;
var query = $"SELECT InstanceName FROM {BrightnessQueryClass} WHERE InstanceName = '{escapedInstanceName}'" ;
2025-10-20 16:22:47 +08:00
var results = connection . CreateQuery ( query ) . ToList ( ) ;
return results . Count > 0 ;
}
catch ( Exception ex )
{
2025-12-03 06:24:20 +08:00
Logger . LogWarning ( $"WMI CanControlMonitor check failed for '{monitor.InstanceName}': {ex.Message}" ) ;
2025-10-20 16:22:47 +08:00
return false ;
}
} ,
cancellationToken ) ;
}
/// <summary>
/// Get monitor brightness
/// </summary>
public async Task < BrightnessInfo > GetBrightnessAsync ( Monitor monitor , CancellationToken cancellationToken = default )
{
Add GPO rule and profile management for PowerDisplay
Introduced a new GPO rule to manage PowerDisplay's enabled state via Group Policy, including updates to `GPOWrapper` and policy files (`PowerToys.admx` and `PowerToys.adml`).
Enhanced the PowerDisplay UI with profile management features, including quick apply, add, edit, and delete functionality. Updated `MainWindow.xaml` and `PowerDisplayPage.xaml` to support these changes, and added localized strings for improved accessibility.
Refactored `MainViewModel` to include a `Profiles` collection, `HasProfiles` property, and `ApplyProfileCommand`. Added methods to load profiles from disk and signal updates to PowerDisplay.
Improved error handling in `DdcCiController` and `WmiController` with input validation and WMI error classification. Optimized handle cleanup in `PhysicalMonitorHandleManager` with a more efficient algorithm.
Refactored `dllmain.cpp` to prevent duplicate PowerDisplay process launches. Updated initialization logic in `MainWindow.xaml.cs` to ensure proper ViewModel setup.
Localized strings for tooltips, warnings, and dialogs. Improved async behavior, logging, and UI accessibility.
2025-12-01 04:32:29 +08:00
ArgumentNullException . ThrowIfNull ( monitor ) ;
2025-10-20 16:22:47 +08:00
return await Task . Run (
( ) = >
{
try
{
using var connection = new WmiConnection ( WmiNamespace ) ;
2025-12-03 06:24:20 +08:00
// Filter by InstanceName to target the specific monitor
var escapedInstanceName = EscapeWmiString ( monitor . InstanceName ) ;
var query = $"SELECT CurrentBrightness FROM {BrightnessQueryClass} WHERE InstanceName = '{escapedInstanceName}'" ;
2025-10-20 16:22:47 +08:00
var results = connection . CreateQuery ( query ) ;
foreach ( var obj in results )
{
var currentBrightness = obj . GetPropertyValue < byte > ( "CurrentBrightness" ) ;
return new BrightnessInfo ( currentBrightness , 0 , 100 ) ;
}
2025-12-03 06:24:20 +08:00
// No match found - monitor may have been disconnected
Logger . LogDebug ( $"WMI GetBrightness: No monitor found with InstanceName '{monitor.InstanceName}'" ) ;
2025-10-20 16:22:47 +08:00
}
catch ( WmiException ex )
{
Logger . LogWarning ( $"WMI GetBrightness failed: {ex.Message} (HResult: 0x{ex.HResult:X})" ) ;
}
catch ( Exception ex )
{
Logger . LogWarning ( $"WMI GetBrightness failed: {ex.Message}" ) ;
}
return BrightnessInfo . Invalid ;
} ,
cancellationToken ) ;
}
/// <summary>
/// Set monitor brightness
/// </summary>
public async Task < MonitorOperationResult > SetBrightnessAsync ( Monitor monitor , int brightness , CancellationToken cancellationToken = default )
{
Add GPO rule and profile management for PowerDisplay
Introduced a new GPO rule to manage PowerDisplay's enabled state via Group Policy, including updates to `GPOWrapper` and policy files (`PowerToys.admx` and `PowerToys.adml`).
Enhanced the PowerDisplay UI with profile management features, including quick apply, add, edit, and delete functionality. Updated `MainWindow.xaml` and `PowerDisplayPage.xaml` to support these changes, and added localized strings for improved accessibility.
Refactored `MainViewModel` to include a `Profiles` collection, `HasProfiles` property, and `ApplyProfileCommand`. Added methods to load profiles from disk and signal updates to PowerDisplay.
Improved error handling in `DdcCiController` and `WmiController` with input validation and WMI error classification. Optimized handle cleanup in `PhysicalMonitorHandleManager` with a more efficient algorithm.
Refactored `dllmain.cpp` to prevent duplicate PowerDisplay process launches. Updated initialization logic in `MainWindow.xaml.cs` to ensure proper ViewModel setup.
Localized strings for tooltips, warnings, and dialogs. Improved async behavior, logging, and UI accessibility.
2025-12-01 04:32:29 +08:00
ArgumentNullException . ThrowIfNull ( monitor ) ;
2025-10-20 16:22:47 +08:00
// Validate brightness range
brightness = Math . Clamp ( brightness , 0 , 100 ) ;
return await Task . Run (
( ) = >
{
try
{
2025-12-03 06:24:20 +08:00
using var connection = new WmiConnection ( WmiNamespace ) ;
2025-10-20 16:22:47 +08:00
2025-12-03 06:24:20 +08:00
// Filter by InstanceName to target the specific monitor
var escapedInstanceName = EscapeWmiString ( monitor . InstanceName ) ;
var query = $"SELECT * FROM {BrightnessMethodClass} WHERE InstanceName = '{escapedInstanceName}'" ;
var results = connection . CreateQuery ( query ) ;
2025-10-20 16:22:47 +08:00
2025-12-03 06:24:20 +08:00
foreach ( var obj in results )
{
// Call WmiSetBrightness method
// Parameters: Timeout (uint32), Brightness (uint8)
// Note: WmiLight requires string values for method parameters
using ( WmiMethod method = obj . GetMethod ( "WmiSetBrightness" ) )
using ( WmiMethodParameters inParams = method . CreateInParameters ( ) )
2025-10-20 16:22:47 +08:00
{
2025-12-03 06:24:20 +08:00
inParams . SetPropertyValue ( "Timeout" , "0" ) ;
inParams . SetPropertyValue ( "Brightness" , brightness . ToString ( CultureInfo . InvariantCulture ) ) ;
uint result = obj . ExecuteMethod < uint > (
method ,
inParams ,
out WmiMethodParameters outParams ) ;
// Check return value (0 indicates success)
if ( result = = 0 )
{
return MonitorOperationResult . Success ( ) ;
}
else
{
return MonitorOperationResult . Failure ( $"WMI method returned error code: {result}" , ( int ) result ) ;
}
2025-10-20 16:22:47 +08:00
}
}
2025-12-03 06:24:20 +08:00
// No match found - monitor may have been disconnected
Logger . LogWarning ( $"WMI SetBrightness: No monitor found with InstanceName '{monitor.InstanceName}'" ) ;
return MonitorOperationResult . Failure ( $"No WMI brightness method found for monitor '{monitor.InstanceName}'" ) ;
}
catch ( UnauthorizedAccessException )
{
return MonitorOperationResult . Failure ( "Access denied. Administrator privileges may be required." , 5 ) ;
}
catch ( WmiException ex )
{
return ClassifyWmiError ( ex , "SetBrightness" ) ;
}
catch ( Exception ex )
{
return MonitorOperationResult . Failure ( $"Unexpected error during SetBrightness: {ex.Message}" ) ;
}
} ,
2025-10-20 16:22:47 +08:00
cancellationToken ) ;
}
/// <summary>
/// Discover supported monitors
/// </summary>
public async Task < IEnumerable < Monitor > > DiscoverMonitorsAsync ( CancellationToken cancellationToken = default )
{
return await Task . Run (
( ) = >
{
var monitors = new List < Monitor > ( ) ;
try
{
using var connection = new WmiConnection ( WmiNamespace ) ;
// First check if WMI brightness support is available
var brightnessQuery = $"SELECT * FROM {BrightnessQueryClass}" ;
var brightnessResults = connection . CreateQuery ( brightnessQuery ) . ToList ( ) ;
if ( brightnessResults . Count = = 0 )
{
return monitors ;
}
// Get monitor information
var idQuery = $"SELECT * FROM {MonitorIdClass}" ;
var idResults = connection . CreateQuery ( idQuery ) . ToList ( ) ;
var monitorInfos = new Dictionary < string , ( string Name , string InstanceName ) > ( ) ;
foreach ( var obj in idResults )
{
try
{
var instanceName = obj . GetPropertyValue < string > ( "InstanceName" ) ? ? string . Empty ;
var userFriendlyName = GetUserFriendlyName ( obj ) ? ? "Internal Display" ;
if ( ! string . IsNullOrEmpty ( instanceName ) )
{
monitorInfos [ instanceName ] = ( userFriendlyName , instanceName ) ;
}
}
catch ( Exception ex )
{
// Skip problematic entries
Logger . LogDebug ( $"Failed to parse WMI monitor info: {ex.Message}" ) ;
}
}
2025-12-04 19:12:49 +08:00
// Get MonitorDisplayInfo from QueryDisplayConfig - this provides the correct monitor numbers
var monitorDisplayInfos = Drivers . DDC . DdcCiNative . GetAllMonitorDisplayInfo ( ) ;
2025-11-26 19:08:01 +08:00
2025-10-20 16:22:47 +08:00
// Create monitor objects for each supported brightness instance
foreach ( var obj in brightnessResults )
{
try
{
var instanceName = obj . GetPropertyValue < string > ( "InstanceName" ) ? ? string . Empty ;
var currentBrightness = obj . GetPropertyValue < byte > ( "CurrentBrightness" ) ;
var name = "Internal Display" ;
if ( monitorInfos . TryGetValue ( instanceName , out var info ) )
{
name = info . Name ;
}
2025-12-10 13:34:36 +08:00
// Extract EdidId from InstanceName
2025-12-03 06:24:20 +08:00
// e.g., "DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0" -> "BOE0900"
2025-12-10 13:34:36 +08:00
var edidId = ExtractHardwareIdFromInstanceName ( instanceName ) ;
2025-12-03 06:24:20 +08:00
2025-12-10 13:34:36 +08:00
// Get MonitorNumber from QueryDisplayConfig by matching EdidId
2025-12-04 19:12:49 +08:00
// This matches Windows Display Settings "Identify" feature
2025-12-10 13:34:36 +08:00
int monitorNumber = GetMonitorNumberFromDisplayInfo ( edidId , monitorDisplayInfos ) ;
// Generate unique monitor Id: "WMI_{EdidId}_{MonitorNumber}"
string monitorId = ! string . IsNullOrEmpty ( edidId )
? $"WMI_{edidId}_{monitorNumber}"
: $"WMI_Unknown_{monitorNumber}" ;
2025-12-04 19:12:49 +08:00
2025-10-20 16:22:47 +08:00
var monitor = new Monitor
{
2025-12-10 13:34:36 +08:00
Id = monitorId ,
2025-10-20 16:22:47 +08:00
Name = name ,
CurrentBrightness = currentBrightness ,
MinBrightness = 0 ,
MaxBrightness = 100 ,
IsAvailable = true ,
InstanceName = instanceName ,
Capabilities = MonitorCapabilities . Brightness | MonitorCapabilities . Wmi ,
ConnectionType = "Internal" ,
CommunicationMethod = "WMI" ,
2025-12-10 13:34:36 +08:00
Manufacturer = edidId . Length > = 3 ? edidId . Substring ( 0 , 3 ) : "Internal" ,
2025-10-20 16:22:47 +08:00
SupportsColorTemperature = false ,
2025-12-04 19:12:49 +08:00
MonitorNumber = monitorNumber ,
2025-10-20 16:22:47 +08:00
} ;
monitors . Add ( monitor ) ;
}
catch ( Exception ex )
{
// Skip problematic monitors
Logger . LogWarning ( $"Failed to create monitor from WMI data: {ex.Message}" ) ;
}
}
}
catch ( WmiException ex )
{
// Return empty list instead of throwing exception
Logger . LogError ( $"WMI DiscoverMonitors failed: {ex.Message} (HResult: 0x{ex.HResult:X})" ) ;
}
catch ( Exception ex )
{
Logger . LogError ( $"WMI DiscoverMonitors failed: {ex.Message}" ) ;
}
return monitors ;
} ,
cancellationToken ) ;
}
/// <summary>
2025-12-10 10:15:40 +08:00
/// Get user-friendly name from WMI object.
/// WmiMonitorID returns UserFriendlyName as a fixed-size uint16 array buffer,
/// with UserFriendlyNameLength indicating the actual character count.
2025-10-20 16:22:47 +08:00
/// </summary>
private static string? GetUserFriendlyName ( WmiObject monitorObject )
{
try
{
2025-12-10 10:15:40 +08:00
var userFriendlyName = monitorObject . GetPropertyValue < ushort [ ] > ( "UserFriendlyName" ) ;
var nameLength = monitorObject . GetPropertyValue < ushort > ( "UserFriendlyNameLength" ) ;
2025-10-20 16:22:47 +08:00
2025-12-10 10:15:40 +08:00
if ( userFriendlyName ! = null & & nameLength > 0 & & nameLength < = userFriendlyName . Length )
2025-10-20 16:22:47 +08:00
{
2025-12-10 10:15:40 +08:00
// Use UserFriendlyNameLength to extract only valid characters
2025-10-20 16:22:47 +08:00
var chars = userFriendlyName
2025-12-10 10:15:40 +08:00
. Take ( nameLength )
2025-10-20 16:22:47 +08:00
. Select ( c = > ( char ) c )
. ToArray ( ) ;
if ( chars . Length > 0 )
{
return new string ( chars ) . Trim ( ) ;
}
}
}
catch ( Exception ex )
{
Logger . LogDebug ( $"Failed to parse UserFriendlyName: {ex.Message}" ) ;
}
return null ;
}
/// <summary>
/// Check WMI service availability
/// </summary>
public static bool IsWmiAvailable ( )
{
try
{
using var connection = new WmiConnection ( WmiNamespace ) ;
var query = $"SELECT * FROM {BrightnessQueryClass}" ;
var results = connection . CreateQuery ( query ) . ToList ( ) ;
return results . Count > 0 ;
}
Add GPO rule and profile management for PowerDisplay
Introduced a new GPO rule to manage PowerDisplay's enabled state via Group Policy, including updates to `GPOWrapper` and policy files (`PowerToys.admx` and `PowerToys.adml`).
Enhanced the PowerDisplay UI with profile management features, including quick apply, add, edit, and delete functionality. Updated `MainWindow.xaml` and `PowerDisplayPage.xaml` to support these changes, and added localized strings for improved accessibility.
Refactored `MainViewModel` to include a `Profiles` collection, `HasProfiles` property, and `ApplyProfileCommand`. Added methods to load profiles from disk and signal updates to PowerDisplay.
Improved error handling in `DdcCiController` and `WmiController` with input validation and WMI error classification. Optimized handle cleanup in `PhysicalMonitorHandleManager` with a more efficient algorithm.
Refactored `dllmain.cpp` to prevent duplicate PowerDisplay process launches. Updated initialization logic in `MainWindow.xaml.cs` to ensure proper ViewModel setup.
Localized strings for tooltips, warnings, and dialogs. Improved async behavior, logging, and UI accessibility.
2025-12-01 04:32:29 +08:00
catch ( WmiException ex ) when ( IsExpectedUnsupportedError ( ex ) )
2025-10-20 16:22:47 +08:00
{
// Expected on systems without WMI brightness support (desktops, some laptops)
Logger . LogInfo ( "WMI brightness control not supported on this system (expected for desktops)" ) ;
return false ;
}
Add GPO rule and profile management for PowerDisplay
Introduced a new GPO rule to manage PowerDisplay's enabled state via Group Policy, including updates to `GPOWrapper` and policy files (`PowerToys.admx` and `PowerToys.adml`).
Enhanced the PowerDisplay UI with profile management features, including quick apply, add, edit, and delete functionality. Updated `MainWindow.xaml` and `PowerDisplayPage.xaml` to support these changes, and added localized strings for improved accessibility.
Refactored `MainViewModel` to include a `Profiles` collection, `HasProfiles` property, and `ApplyProfileCommand`. Added methods to load profiles from disk and signal updates to PowerDisplay.
Improved error handling in `DdcCiController` and `WmiController` with input validation and WMI error classification. Optimized handle cleanup in `PhysicalMonitorHandleManager` with a more efficient algorithm.
Refactored `dllmain.cpp` to prevent duplicate PowerDisplay process launches. Updated initialization logic in `MainWindow.xaml.cs` to ensure proper ViewModel setup.
Localized strings for tooltips, warnings, and dialogs. Improved async behavior, logging, and UI accessibility.
2025-12-01 04:32:29 +08:00
catch ( WmiException ex )
{
// Unexpected WMI error - log with details for debugging
Logger . LogWarning ( $"WMI availability check failed: {ex.Message} (HResult: 0x{ex.HResult:X})" ) ;
return false ;
}
2025-10-20 16:22:47 +08:00
catch ( Exception ex )
{
Add GPO rule and profile management for PowerDisplay
Introduced a new GPO rule to manage PowerDisplay's enabled state via Group Policy, including updates to `GPOWrapper` and policy files (`PowerToys.admx` and `PowerToys.adml`).
Enhanced the PowerDisplay UI with profile management features, including quick apply, add, edit, and delete functionality. Updated `MainWindow.xaml` and `PowerDisplayPage.xaml` to support these changes, and added localized strings for improved accessibility.
Refactored `MainViewModel` to include a `Profiles` collection, `HasProfiles` property, and `ApplyProfileCommand`. Added methods to load profiles from disk and signal updates to PowerDisplay.
Improved error handling in `DdcCiController` and `WmiController` with input validation and WMI error classification. Optimized handle cleanup in `PhysicalMonitorHandleManager` with a more efficient algorithm.
Refactored `dllmain.cpp` to prevent duplicate PowerDisplay process launches. Updated initialization logic in `MainWindow.xaml.cs` to ensure proper ViewModel setup.
Localized strings for tooltips, warnings, and dialogs. Improved async behavior, logging, and UI accessibility.
2025-12-01 04:32:29 +08:00
// Unexpected non-WMI error
2025-10-20 16:22:47 +08:00
Logger . LogDebug ( $"WMI availability check failed: {ex.Message}" ) ;
return false ;
}
}
// Extended features not supported by WMI
public Task < MonitorOperationResult > SetContrastAsync ( Monitor monitor , int contrast , CancellationToken cancellationToken = default )
{
return Task . FromResult ( MonitorOperationResult . Failure ( "Contrast control not supported via WMI" ) ) ;
}
public Task < MonitorOperationResult > SetVolumeAsync ( Monitor monitor , int volume , CancellationToken cancellationToken = default )
{
return Task . FromResult ( MonitorOperationResult . Failure ( "Volume control not supported via WMI" ) ) ;
}
public Task < BrightnessInfo > GetColorTemperatureAsync ( Monitor monitor , CancellationToken cancellationToken = default )
{
return Task . FromResult ( BrightnessInfo . Invalid ) ;
}
public Task < MonitorOperationResult > SetColorTemperatureAsync ( Monitor monitor , int colorTemperature , CancellationToken cancellationToken = default )
{
return Task . FromResult ( MonitorOperationResult . Failure ( "Color temperature control not supported via WMI" ) ) ;
}
2025-11-27 14:51:31 +08:00
public Task < BrightnessInfo > GetInputSourceAsync ( Monitor monitor , CancellationToken cancellationToken = default )
{
// Input source switching not supported for internal displays
return Task . FromResult ( BrightnessInfo . Invalid ) ;
}
public Task < MonitorOperationResult > SetInputSourceAsync ( Monitor monitor , int inputSource , CancellationToken cancellationToken = default )
{
// Input source switching not supported for internal displays
return Task . FromResult ( MonitorOperationResult . Failure ( "Input source switching not supported via WMI" ) ) ;
}
2025-10-20 16:22:47 +08:00
public Task < string > GetCapabilitiesStringAsync ( Monitor monitor , CancellationToken cancellationToken = default )
{
return Task . FromResult ( string . Empty ) ;
}
public void Dispose ( )
{
Dispose ( true ) ;
GC . SuppressFinalize ( this ) ;
}
protected virtual void Dispose ( bool disposing )
{
if ( ! _disposed & & disposing )
{
// WmiLight objects are automatically cleaned up, no specific cleanup needed here
_disposed = true ;
}
}
}
}