Refactor monitor matching logic

Replaced legacy parsing and matching methods with a streamlined approach using `QueryDisplayConfig` and `MonitorDisplayInfo` to align monitor numbering with Windows Display Settings' "Identify" feature. Removed redundant methods and tests for parsing display numbers and device paths. Introduced a `MonitorNumber` property in `MonitorDisplayInfo` for consistent numbering. Updated `MonitorDiscoveryHelper` and `WmiController` to use the new logic. Enhanced logging for better debugging and maintainability.
This commit is contained in:
Yu Leng
2025-12-04 19:12:49 +08:00
parent 6c7504d134
commit fc0ae601a6
5 changed files with 63 additions and 598 deletions

View File

@@ -2,282 +2,18 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using PowerDisplay.Common.Drivers.DDC;
using PowerDisplay.Common.Utils; using PowerDisplay.Common.Utils;
namespace PowerDisplay.UnitTests; namespace PowerDisplay.UnitTests;
/// <summary> /// <summary>
/// Unit tests for MonitorMatchingHelper class. /// Unit tests for MonitorMatchingHelper class.
/// Tests parsing logic for monitor numbers and device matching. /// Tests monitor key generation and matching logic.
/// </summary> /// </summary>
[TestClass] [TestClass]
public class MonitorMatchingHelperTests public class MonitorMatchingHelperTests
{ {
[TestMethod]
[DataRow(@"\\.\DISPLAY1", 1)]
[DataRow(@"\\.\DISPLAY2", 2)]
[DataRow(@"\\.\DISPLAY10", 10)]
[DataRow(@"\\.\DISPLAY99", 99)]
public void ParseDisplayNumber_ValidAdapterName_ReturnsCorrectNumber(string adapterName, int expected)
{
// Act
var result = MonitorMatchingHelper.ParseDisplayNumber(adapterName);
// Assert
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow("DISPLAY1", 1)]
[DataRow("DISPLAY2", 2)]
[DataRow("display3", 3)]
[DataRow("Display10", 10)]
public void ParseDisplayNumber_WithoutPrefix_ReturnsCorrectNumber(string adapterName, int expected)
{
// Act
var result = MonitorMatchingHelper.ParseDisplayNumber(adapterName);
// Assert
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow(null, 0)]
[DataRow("", 0)]
[DataRow(" ", 0)]
public void ParseDisplayNumber_NullOrEmpty_ReturnsZero(string? adapterName, int expected)
{
// Act
var result = MonitorMatchingHelper.ParseDisplayNumber(adapterName!);
// Assert
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow("MONITOR1", 0)]
[DataRow("SCREEN1", 0)]
[DataRow("InvalidName", 0)]
[DataRow(@"\\.\MONITOR1", 0)]
public void ParseDisplayNumber_NoDisplayKeyword_ReturnsZero(string adapterName, int expected)
{
// Act
var result = MonitorMatchingHelper.ParseDisplayNumber(adapterName);
// Assert
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow(@"\\.\DISPLAY", 0)]
[DataRow("DISPLAY", 0)]
[DataRow("DISPLAYabc", 0)]
public void ParseDisplayNumber_NoNumberAfterDisplay_ReturnsZero(string adapterName, int expected)
{
// Act
var result = MonitorMatchingHelper.ParseDisplayNumber(adapterName);
// Assert
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\LGD05E5\4&abcdef12&0&UID12345_0", "4&abcdef12&0&UID12345")]
[DataRow(@"DISPLAY\HPN360C\5&2c03a83e&0&UID262_1", "5&2c03a83e&0&UID262")]
[DataRow(@"DISPLAY\DELL\4&other&0&UID999_12", "4&other&0&UID999")]
[DataRow(@"DISPLAY\TEST\path_99", "path")]
public void ExtractDeviceInstancePath_ValidInstanceNameWithSuffix_ReturnsPathWithoutSuffix(string instanceName, string expected)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\TEST\devicepath", "devicepath")]
[DataRow(@"DISPLAY\TEST\path_abc", "path_abc")] // Non-digit suffix should not be removed
[DataRow(@"DISPLAY\TEST\path_", "path_")] // Empty suffix should not be removed
[DataRow(@"DISPLAY\TEST\path_0x1", "path_0x1")] // Mixed suffix should not be removed
public void ExtractDeviceInstancePath_ValidInstanceNameWithoutSuffix_ReturnsPath(string instanceName, string expected)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.AreEqual(expected, result);
}
[TestMethod]
[DataRow(null)]
[DataRow("")]
public void ExtractDeviceInstancePath_NullOrEmpty_ReturnsNull(string? instanceName)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName!);
// Assert
Assert.IsNull(result);
}
[TestMethod]
[DataRow("NoBackslashInName")]
[DataRow("SingleSegment")]
public void ExtractDeviceInstancePath_NoBackslash_ReturnsNull(string instanceName)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.IsNull(result);
}
[TestMethod]
[DataRow(@"DISPLAY\BOE0900\")]
public void ExtractDeviceInstancePath_TrailingBackslash_ReturnsNull(string instanceName)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.IsNull(result);
}
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_MatchingDevice_ReturnsMonitorNumber()
{
// Arrange
var instanceName = @"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0";
var displayDevices = new List<DisplayDeviceInfo>
{
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY1",
DeviceKey = @"\\?\DISPLAY#BOE0900#4&10fd3ab1&0&UID265988#{some-guid}",
},
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY2",
DeviceKey = @"\\?\DISPLAY#DELL#4&other&0&UID999#{some-guid}",
},
};
// Act
var result = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
// Assert
Assert.AreEqual(1, result);
}
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_SecondDevice_ReturnsCorrectNumber()
{
// Arrange
var instanceName = @"DISPLAY\DELL\4&other&0&UID999_0";
var displayDevices = new List<DisplayDeviceInfo>
{
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY1",
DeviceKey = @"\\?\DISPLAY#BOE0900#4&10fd3ab1&0&UID265988#{some-guid}",
},
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY2",
DeviceKey = @"\\?\DISPLAY#DELL#4&other&0&UID999#{some-guid}",
},
};
// Act
var result = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
// Assert
Assert.AreEqual(2, result);
}
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_NoMatchingDevice_ReturnsZero()
{
// Arrange
var instanceName = @"DISPLAY\UNKNOWN\nonexistent_0";
var displayDevices = new List<DisplayDeviceInfo>
{
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY1",
DeviceKey = @"\\?\DISPLAY#BOE0900#4&10fd3ab1&0&UID265988#{some-guid}",
},
};
// Act
var result = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
// Assert
Assert.AreEqual(0, result);
}
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_EmptyDeviceList_ReturnsZero()
{
// Arrange
var instanceName = @"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0";
var displayDevices = new List<DisplayDeviceInfo>();
// Act
var result = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
// Assert
Assert.AreEqual(0, result);
}
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_InvalidInstanceName_ReturnsZero()
{
// Arrange
var instanceName = "InvalidInstanceName";
var displayDevices = new List<DisplayDeviceInfo>
{
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY1",
DeviceKey = @"\\?\DISPLAY#BOE0900#4&10fd3ab1&0&UID265988#{some-guid}",
},
};
// Act
var result = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
// Assert
Assert.AreEqual(0, result);
}
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_CaseInsensitiveMatch_ReturnsMonitorNumber()
{
// Arrange
var instanceName = @"DISPLAY\boe0900\4&10FD3AB1&0&uid265988_0";
var displayDevices = new List<DisplayDeviceInfo>
{
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY1",
DeviceKey = @"\\?\DISPLAY#BOE0900#4&10fd3ab1&0&UID265988#{some-guid}",
},
};
// Act
var result = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
// Assert
Assert.AreEqual(1, result);
}
[TestMethod] [TestMethod]
public void GetMonitorKey_WithHardwareId_ReturnsHardwareId() public void GetMonitorKey_WithHardwareId_ReturnsHardwareId()
{ {
@@ -347,183 +83,4 @@ public class MonitorMatchingHelperTests
// Assert // Assert
Assert.AreEqual(internalName, result); Assert.AreEqual(internalName, result);
} }
/// <summary>
/// Tests that WMI instance index suffixes (_0, _1, _2, etc.) are correctly removed.
/// WMI appends "_N" where N is the instance index to device instance paths.
/// See: https://learn.microsoft.com/en-us/windows/win32/wmicoreprov/wmimonitorid
/// </summary>
[TestMethod]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_1", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_2", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_9", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_10", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_99", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_100", "4&10fd3ab1&0&UID265988")]
[DataRow(@"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_999", "4&10fd3ab1&0&UID265988")]
public void ExtractDeviceInstancePath_WmiInstanceIndexSuffix_RemovesSuffix(string instanceName, string expected)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.AreEqual(expected, result);
}
/// <summary>
/// Tests real-world WMI InstanceName formats from various monitor manufacturers.
/// These formats are documented at: https://learn.microsoft.com/en-us/windows/win32/wmicoreprov/wmimonitorid
/// </summary>
[TestMethod]
[DataRow(@"DISPLAY\HPN360C\5&2c03a83e&0&UID262_0", "5&2c03a83e&0&UID262")] // HP monitor
[DataRow(@"DISPLAY\HWP2868\5&3eb7fbc&0&UID16777472_0", "5&3eb7fbc&0&UID16777472")] // HP monitor
[DataRow(@"DISPLAY\ENC2530\4&307c4481&0&UID224795_0", "4&307c4481&0&UID224795")] // EIZO monitor
[DataRow(@"DISPLAY\DEL0000\0&00000000&0&UID0000_0", "0&00000000&0&UID0000")] // Dell monitor
[DataRow(@"DISPLAY\SAM0382\4&38b6bd55&0&UID198147_0", "4&38b6bd55&0&UID198147")] // Samsung monitor
[DataRow(@"DISPLAY\GSM5C6D\5&1234abcd&0&UID999_0", "5&1234abcd&0&UID999")] // LG monitor
[DataRow(@"DISPLAY\ACI27F6\4&deadbeef&0&UID12345_0", "4&deadbeef&0&UID12345")] // ASUS monitor
public void ExtractDeviceInstancePath_RealWorldFormats_ExtractsCorrectly(string instanceName, string expected)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.AreEqual(expected, result);
}
/// <summary>
/// Tests that non-numeric suffixes are preserved (not removed).
/// Only pure numeric suffixes after underscore should be removed.
/// </summary>
[TestMethod]
[DataRow(@"DISPLAY\TEST\path_abc", "path_abc")]
[DataRow(@"DISPLAY\TEST\path_0a", "path_0a")]
[DataRow(@"DISPLAY\TEST\path_a0", "path_a0")]
[DataRow(@"DISPLAY\TEST\path_0x1", "path_0x1")]
[DataRow(@"DISPLAY\TEST\path_1x", "path_1x")]
[DataRow(@"DISPLAY\TEST\path_", "path_")]
[DataRow(@"DISPLAY\TEST\path_ ", "path_ ")]
public void ExtractDeviceInstancePath_NonNumericSuffix_PreservesSuffix(string instanceName, string expected)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.AreEqual(expected, result);
}
/// <summary>
/// Tests paths with multiple underscores - only the last numeric suffix should be removed.
/// </summary>
[TestMethod]
[DataRow(@"DISPLAY\TEST\a_b_0", "a_b")]
[DataRow(@"DISPLAY\TEST\a_b_c_1", "a_b_c")]
[DataRow(@"DISPLAY\TEST\path_with_underscores_99", "path_with_underscores")]
[DataRow(@"DISPLAY\TEST\UID_265988_0", "UID_265988")]
public void ExtractDeviceInstancePath_MultipleUnderscores_RemovesOnlyLastNumericSuffix(string instanceName, string expected)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.AreEqual(expected, result);
}
/// <summary>
/// Tests edge cases where underscore is at the beginning.
/// </summary>
[TestMethod]
[DataRow(@"DISPLAY\TEST\_0", "_0")] // Underscore at position 0, should not be removed
[DataRow(@"DISPLAY\TEST\_123", "_123")] // Underscore at position 0, should not be removed
public void ExtractDeviceInstancePath_UnderscoreAtStart_PreservesPath(string instanceName, string expected)
{
// Act
var result = MonitorMatchingHelper.ExtractDeviceInstancePath(instanceName);
// Assert
Assert.AreEqual(expected, result);
}
/// <summary>
/// Tests that GetMonitorNumberFromWmiInstanceName works correctly with non-zero WMI instance indices.
/// This verifies the fix for handling _1, _2, etc. suffixes instead of just _0.
/// </summary>
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_WithInstanceIndex1_ReturnsMonitorNumber()
{
// Arrange - Using _1 suffix instead of _0
var instanceName = @"DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_1";
var displayDevices = new List<DisplayDeviceInfo>
{
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY1",
DeviceKey = @"\\?\DISPLAY#BOE0900#4&10fd3ab1&0&UID265988#{some-guid}",
},
};
// Act
var result = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
// Assert
Assert.AreEqual(1, result);
}
/// <summary>
/// Tests matching with multi-digit WMI instance index.
/// </summary>
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_WithInstanceIndex12_ReturnsMonitorNumber()
{
// Arrange - Using _12 suffix
var instanceName = @"DISPLAY\DELL\4&abcdef&0&UID999_12";
var displayDevices = new List<DisplayDeviceInfo>
{
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY3",
DeviceKey = @"\\?\DISPLAY#DELL#4&abcdef&0&UID999#{guid}",
},
};
// Act
var result = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
// Assert
Assert.AreEqual(3, result);
}
/// <summary>
/// Tests that multiple monitors with different WMI instance indices match correctly.
/// Simulates a scenario with duplicate monitor models where WMI assigns different indices.
/// </summary>
[TestMethod]
public void GetMonitorNumberFromWmiInstanceName_MultipleMonitorsSameModel_MatchesCorrectly()
{
// Arrange - Two monitors of same model but different UIDs
var displayDevices = new List<DisplayDeviceInfo>
{
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY1",
DeviceKey = @"\\?\DISPLAY#BOE0900#4&10fd3ab1&0&UID100#{guid}",
},
new DisplayDeviceInfo
{
AdapterName = @"\\.\DISPLAY2",
DeviceKey = @"\\?\DISPLAY#BOE0900#4&10fd3ab1&0&UID200#{guid}",
},
};
// Act & Assert - First monitor with _0 suffix
var result1 = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(
@"DISPLAY\BOE0900\4&10fd3ab1&0&UID100_0", displayDevices);
Assert.AreEqual(1, result1);
// Act & Assert - Second monitor with _0 suffix (different UID)
var result2 = MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(
@"DISPLAY\BOE0900\4&10fd3ab1&0&UID200_0", displayDevices);
Assert.AreEqual(2, result2);
}
} }

View File

@@ -482,6 +482,7 @@ namespace PowerDisplay.Common.Drivers.DDC
} }
// Get information for each path // Get information for each path
// The path index corresponds to Windows Display Settings "Identify" number
for (int i = 0; i < pathCount; i++) for (int i = 0; i < pathCount; i++)
{ {
var path = paths[i]; var path = paths[i];
@@ -497,7 +498,10 @@ namespace PowerDisplay.Common.Drivers.DDC
HardwareId = hardwareId ?? string.Empty, HardwareId = hardwareId ?? string.Empty,
AdapterId = path.TargetInfo.AdapterId, AdapterId = path.TargetInfo.AdapterId,
TargetId = path.TargetInfo.Id, TargetId = path.TargetInfo.Id,
MonitorNumber = i + 1, // 1-based, matches Windows Display Settings
}; };
Logger.LogDebug($"QueryDisplayConfig path[{i}]: HardwareId={hardwareId}, FriendlyName={friendlyName}, MonitorNumber={i + 1}");
} }
} }
} }
@@ -610,5 +614,12 @@ namespace PowerDisplay.Common.Drivers.DDC
public LUID AdapterId { get; set; } public LUID AdapterId { get; set; }
public uint TargetId { get; set; } public uint TargetId { get; set; }
/// <summary>
/// Gets or sets the monitor number based on QueryDisplayConfig path index.
/// This matches the number shown in Windows Display Settings "Identify" feature.
/// 1-based index (paths[0] = 1, paths[1] = 2, etc.)
/// </summary>
public int MonitorNumber { get; set; }
} }
} }

View File

@@ -220,7 +220,7 @@ namespace PowerDisplay.Common.Drivers.DDC
CommunicationMethod = "DDC/CI", CommunicationMethod = "DDC/CI",
Manufacturer = ExtractManufacturer(name), Manufacturer = ExtractManufacturer(name),
CapabilitiesStatus = "unknown", CapabilitiesStatus = "unknown",
MonitorNumber = MonitorMatchingHelper.ParseDisplayNumber(adapterName), MonitorNumber = GetMonitorNumber(matchedInfo),
Orientation = GetMonitorOrientation(adapterName), Orientation = GetMonitorOrientation(adapterName),
}; };
@@ -307,6 +307,21 @@ namespace PowerDisplay.Common.Drivers.DDC
return firstWord.Length > 2 ? firstWord : "Unknown"; return firstWord.Length > 2 ? firstWord : "Unknown";
} }
/// <summary>
/// Get monitor number from MonitorDisplayInfo (QueryDisplayConfig path index).
/// This matches the number shown in Windows Display Settings "Identify" feature.
/// </summary>
private int GetMonitorNumber(MonitorDisplayInfo? matchedInfo)
{
if (matchedInfo.HasValue && matchedInfo.Value.MonitorNumber > 0)
{
return matchedInfo.Value.MonitorNumber;
}
// No match found - return 0 (will not display number suffix)
return 0;
}
/// <summary> /// <summary>
/// Get monitor orientation using EnumDisplaySettings /// Get monitor orientation using EnumDisplaySettings
/// </summary> /// </summary>

View File

@@ -106,6 +106,34 @@ namespace PowerDisplay.Common.Drivers.WMI
return string.Empty; return string.Empty;
} }
/// <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;
}
public string Name => "WMI Monitor Controller (WmiLight)"; public string Name => "WMI Monitor Controller (WmiLight)";
/// <summary> /// <summary>
@@ -309,8 +337,8 @@ namespace PowerDisplay.Common.Drivers.WMI
} }
} }
// Pre-fetch display devices once to avoid repeated Win32 API calls in the loop // Get MonitorDisplayInfo from QueryDisplayConfig - this provides the correct monitor numbers
var displayDevices = Drivers.DDC.DdcCiNative.GetAllDisplayDevices(); var monitorDisplayInfos = Drivers.DDC.DdcCiNative.GetAllMonitorDisplayInfo();
// Create monitor objects for each supported brightness instance // Create monitor objects for each supported brightness instance
foreach (var obj in brightnessResults) foreach (var obj in brightnessResults)
@@ -330,6 +358,10 @@ namespace PowerDisplay.Common.Drivers.WMI
// e.g., "DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0" -> "BOE0900" // e.g., "DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0" -> "BOE0900"
var hardwareId = ExtractHardwareIdFromInstanceName(instanceName); var hardwareId = ExtractHardwareIdFromInstanceName(instanceName);
// Get MonitorNumber from QueryDisplayConfig by matching HardwareId
// This matches Windows Display Settings "Identify" feature
int monitorNumber = GetMonitorNumberFromDisplayInfo(hardwareId, monitorDisplayInfos);
var monitor = new Monitor var monitor = new Monitor
{ {
Id = $"WMI_{instanceName}", Id = $"WMI_{instanceName}",
@@ -345,7 +377,7 @@ namespace PowerDisplay.Common.Drivers.WMI
CommunicationMethod = "WMI", CommunicationMethod = "WMI",
Manufacturer = hardwareId.Length >= 3 ? hardwareId.Substring(0, 3) : "Internal", Manufacturer = hardwareId.Length >= 3 ? hardwareId.Substring(0, 3) : "Internal",
SupportsColorTemperature = false, SupportsColorTemperature = false,
MonitorNumber = Utils.MonitorMatchingHelper.GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices), MonitorNumber = monitorNumber,
}; };
monitors.Add(monitor); monitors.Add(monitor);

View File

@@ -3,10 +3,6 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Collections.Generic;
using System.Linq;
using ManagedCommon;
using PowerDisplay.Common.Drivers.DDC;
using PowerDisplay.Common.Interfaces; using PowerDisplay.Common.Interfaces;
namespace PowerDisplay.Common.Utils namespace PowerDisplay.Common.Utils
@@ -17,152 +13,6 @@ namespace PowerDisplay.Common.Utils
/// </summary> /// </summary>
public static class MonitorMatchingHelper public static class MonitorMatchingHelper
{ {
/// <summary>
/// Get monitor number from WMI InstanceName by matching with EnumDisplayDevices.
/// Returns 0 if matching fails.
/// </summary>
/// <param name="instanceName">WMI InstanceName (e.g., "DISPLAY\BOE0900\4&amp;10fd3ab1&amp;0&amp;UID265988_0")</param>
/// <returns>Monitor number (1, 2, 3...) or 0 if not found</returns>
public static int GetMonitorNumberFromWmiInstanceName(string instanceName)
{
// Fetch display devices and delegate to the overload
var displayDevices = DdcCiNative.GetAllDisplayDevices();
return GetMonitorNumberFromWmiInstanceName(instanceName, displayDevices);
}
/// <summary>
/// Get monitor number from WMI InstanceName using pre-fetched display devices.
/// Use this overload in loops to avoid repeated Win32 API calls.
/// Returns 0 if matching fails.
/// </summary>
/// <param name="instanceName">WMI InstanceName (e.g., "DISPLAY\BOE0900\4&amp;10fd3ab1&amp;0&amp;UID265988_0")</param>
/// <param name="displayDevices">Pre-fetched list of display devices from DdcCiNative.GetAllDisplayDevices()</param>
/// <returns>Monitor number (1, 2, 3...) or 0 if not found</returns>
public static int GetMonitorNumberFromWmiInstanceName(string instanceName, IReadOnlyList<DisplayDeviceInfo> displayDevices)
{
try
{
// Extract the device instance path for matching
string? devicePath = ExtractDeviceInstancePath(instanceName);
if (string.IsNullOrEmpty(devicePath))
{
Logger.LogWarning($"GetMonitorNumberFromWmiInstanceName: Failed to extract device path from '{instanceName}'");
return 0;
}
if (displayDevices.Count == 0)
{
Logger.LogWarning("GetMonitorNumberFromWmiInstanceName: No display devices found from EnumDisplayDevices");
return 0;
}
// Log all available devices for debugging
Logger.LogDebug($"GetMonitorNumberFromWmiInstanceName: Searching for devicePath='{devicePath}' in {displayDevices.Count} devices");
foreach (var d in displayDevices)
{
Logger.LogDebug($" - Adapter: {d.AdapterName}, DeviceKey: {d.DeviceKey}");
}
// Find matching device by checking if DeviceKey contains our device path
foreach (var device in displayDevices)
{
if (!string.IsNullOrEmpty(device.DeviceKey) &&
device.DeviceKey.Contains(devicePath, StringComparison.OrdinalIgnoreCase))
{
// Found match! Parse monitor number from AdapterName (e.g., "\\.\DISPLAY1" -> 1)
int monitorNumber = ParseDisplayNumber(device.AdapterName);
Logger.LogDebug($"GetMonitorNumberFromWmiInstanceName: Matched '{instanceName}' to DISPLAY{monitorNumber}");
return monitorNumber;
}
}
// No match found
Logger.LogWarning($"GetMonitorNumberFromWmiInstanceName: No matching display device found for path '{devicePath}'");
}
catch (Exception ex)
{
Logger.LogError($"GetMonitorNumberFromWmiInstanceName: Exception while parsing '{instanceName}': {ex.Message}");
}
return 0;
}
/// <summary>
/// Extract the device instance path from WMI InstanceName for matching.
/// WMI InstanceName format: "DISPLAY\BOE0900\4&amp;10fd3ab1&amp;0&amp;UID265988_0"
/// Returns: "4&amp;10fd3ab1&amp;0&amp;UID265988" (the unique device path portion)
/// </summary>
/// <param name="instanceName">WMI InstanceName</param>
/// <returns>Device instance path for matching, or null if extraction fails</returns>
public static string? ExtractDeviceInstancePath(string instanceName)
{
if (string.IsNullOrEmpty(instanceName))
{
Logger.LogDebug("ExtractDeviceInstancePath: instanceName is null or empty");
return null;
}
// Find the last backslash to get the instance path portion
// e.g., "DISPLAY\BOE0900\4&10fd3ab1&0&UID265988_0" -> "4&10fd3ab1&0&UID265988_0"
int lastBackslash = instanceName.LastIndexOf('\\');
if (lastBackslash < 0 || lastBackslash >= instanceName.Length - 1)
{
Logger.LogWarning($"ExtractDeviceInstancePath: Invalid format, no valid backslash found in '{instanceName}'");
return null;
}
string instancePath = instanceName.Substring(lastBackslash + 1);
// Remove trailing WMI instance index suffix (e.g., _0, _1, _12)
// WMI appends "_N" where N is the instance index to device instance paths
// See: https://learn.microsoft.com/en-us/windows/win32/wmicoreprov/wmimonitorid
int lastUnderscore = instancePath.LastIndexOf('_');
if (lastUnderscore > 0)
{
string suffix = instancePath[(lastUnderscore + 1)..];
if (suffix.Length > 0 && suffix.All(char.IsDigit))
{
instancePath = instancePath[..lastUnderscore];
}
}
if (string.IsNullOrEmpty(instancePath))
{
Logger.LogWarning($"ExtractDeviceInstancePath: Extracted path is empty from '{instanceName}'");
return null;
}
return instancePath;
}
/// <summary>
/// Parse display number from adapter name (e.g., "\\.\DISPLAY1" -> 1)
/// </summary>
/// <param name="adapterName">Adapter name from EnumDisplayDevices</param>
/// <returns>Display number or 0 if parsing fails</returns>
public static int ParseDisplayNumber(string adapterName)
{
if (string.IsNullOrEmpty(adapterName))
{
return 0;
}
// Find "DISPLAY" and extract the number after it
int index = adapterName.IndexOf("DISPLAY", StringComparison.OrdinalIgnoreCase);
if (index >= 0)
{
string numberPart = adapterName.Substring(index + 7);
string numberStr = new string(numberPart.TakeWhile(char.IsDigit).ToArray());
if (int.TryParse(numberStr, out int number))
{
return number;
}
}
return 0;
}
/// <summary> /// <summary>
/// Generate a unique key for monitor matching based on hardware ID and internal name. /// Generate a unique key for monitor matching based on hardware ID and internal name.
/// Uses HardwareId if available; otherwise falls back to Id (InternalName) or Name. /// Uses HardwareId if available; otherwise falls back to Id (InternalName) or Name.