我正在尝试将我的 UWP 应用程序转换为 WinUI3,但无法获取监视器信息(我的应用程序窗口所在的位置[或所属])。
我需要知道 UWP 应用程序中的以下值。
var currentViewDisplay = DisplayInformation.GetForCurrentView();
//currentViewDisplay.ScreenHeightInRawPixels
//currentViewDisplay.RawPixelsPerViewPixel
//currentViewDisplay.ScreenWidthInRawPixels
//currentViewDisplay.RawPixelsPerViewPixel;
//currentViewDisplay.DiagonalSizeInInches
//and
//currentViewDisplay.DpiChanged event
我已将 nuget CsWin32 添加到我的项目中,并尝试探索以下本机方法:
GetMonitorInfo
MonitorFromWindow
EnumDisplaySettings
EnumDisplayDevices
DisplayConfigGetDeviceInfo
IDisplayPathInterop
DISPLAYCONFIG_SOURCE_DEVICE_NAME
DISPLAYCONFIG_TARGET_DEVICE_NAME
using (var mgr = DisplayManager.Create(DisplayManagerOptions.None))
{
var state = mgr.TryReadCurrentStateForAllTargets().State;
foreach (var view in state.Views)
{
foreach (var path in view.Paths)
{
var monitor = path.Target.TryGetMonitor();
if (monitor != null)
{
}
}
}
// or
var displayList = await DeviceInformation.FindAllAsync(DisplayMonitor.GetDeviceSelector());
var monitorInfo = await DisplayMonitor.FromInterfaceIdAsync(displayList[0].Id);
所有这些方法都会返回有用的信息,但我不知道如何找到我的窗口所属的监视器。
所以我尝试了下一个,但令人沮丧,因为它不返回任何内容并且只需要标头作为输入。为什么只有标题? CsWin32有问题吗?
var ip = WinRT.CastExtensions.As<winmdroot.System.WinRT.Display.IDisplayPathInterop>(path);
var sourceId = ip.GetSourceId();
var targetName = new winmdroot.Devices.Display.DISPLAYCONFIG_TARGET_DEVICE_NAME();
targetName.header.adapterId.LowPart = monitor.DisplayAdapterId.LowPart;
targetName.header.adapterId.HighPart = monitor.DisplayAdapterId.HighPart;
targetName.header.id = sourceId;
targetName.header.type = winmdroot.Devices.Display.DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
targetName.header.size = (uint)Marshal.SizeOf(typeof(winmdroot.Devices.Display.DISPLAYCONFIG_TARGET_DEVICE_NAME));
var dcgdiR = PInvoke.DisplayConfigGetDeviceInfo(ref targetName.header);
// dcgdiR == 31, what does 31 mean?
我也尝试过,并没有关于显示器物理特性的内容。 监视器是
nint
,所以只是数字...
var displayArea = DisplayArea.GetFromWindowId(window.AppWindow.Id, DisplayAreaFallback.Nearest);
var dispalyInfo = Microsoft.Graphics.Display.DisplayInformation.CreateForDisplayId(displayArea.DisplayId);
var monitor = Win32Interop.GetMonitorFromDisplayId(displayArea.DisplayId);
然后我尝试了下一个,我希望(我希望,不确定)它与我的应用程序窗口的显示器有关,并且有一些关于分辨率和 dpi 的数据,但没有关于 DiagonalSizeInInches。
var devmodew = new winmdroot.Graphics.Gdi.DEVMODEW();
PInvoke.EnumDisplaySettings(null, winmdroot.Graphics.Gdi.ENUM_DISPLAY_SETTINGS_MODE.ENUM_CURRENT_SETTINGS, ref devmodew);
然后我尝试了下一个,
dv
props 有点 0 左右,没有什么用处。
var dv = new winmdroot.Graphics.Gdi.DISPLAY_DEVICEW();
uint devN = 0;
while (PInvoke.EnumDisplayDevices(null, devN, ref dv, 0) == 1)
{
devN++;
}
为什么我错过了将所有这些关联起来? 还有其他方法吗?
物理监视器可以通过以下方式识别:
注意:监视器句柄与物理监视器句柄不同,从逻辑上讲,监视器句柄可以与多个物理监视器句柄关联(请参阅GetPhysicalMonitorsFromHMONITOR)。我不知道这种事什么时候发生,但我敢打赌这种情况很少见。这同样适用于显示名称。设备 ID 或设备路径通常可转换为设备实例 ID。
如何使用上述信息关联监视器上来自各种 API 的信息就像是一个谜题。
你已经有了
DisplayMonitor
,它的DeviceId就是设备ID。然后,您可以通过 MonitorFromWindow 或 MonitorFromPoint 或 MonitorFromRect 函数获取监视器句柄。接下来是连接设备 ID 和监视器句柄。其中一种方法是结合传统的 EnumDisplayDevices 和 EnumDisplayMonitors 以及 GetMonitorInfo 函数。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
internal static class DeviceHelper
{
public static IEnumerable<(IntPtr monitorHandle, string displayName, string deviceId, string deviceInstanceId)> EnumerateDisplayDeviceMonitorPairs()
{
var devices = GetDisplayDevices();
foreach (var monitor in GetDisplayMonitors())
{
foreach (var device in devices.Where(x => x.displayDeviceName == monitor.displayDeviceName))
{
var deviceInstanceId = ConvertToDeviceInstanceId(device.monitorDeviceId);
yield return (monitor.monitorHandle, device.displayDeviceName, device.monitorDeviceId, deviceInstanceId);
}
}
}
private static (string displayDeviceName, string monitorDeviceId)[] GetDisplayDevices()
{
var list = new List<(string, string)>();
var size = (uint)Marshal.SizeOf<DISPLAY_DEVICE>();
var display = new DISPLAY_DEVICE { cb = size };
var monitor = new DISPLAY_DEVICE { cb = size };
for (uint i = 0; EnumDisplayDevices(null, i, ref display, EDD_GET_DEVICE_INTERFACE_NAME); i++)
{
for (uint j = 0; EnumDisplayDevices(display.DeviceName, j, ref monitor, EDD_GET_DEVICE_INTERFACE_NAME); j++)
{
list.Add((display.DeviceName, monitor.DeviceID));
}
}
return list.ToArray();
}
private static (string displayDeviceName, IntPtr monitorHandle)[] GetDisplayMonitors()
{
var list = new List<(string, IntPtr)>();
var size = (uint)Marshal.SizeOf<MONITORINFOEX>();
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
(monitorHandle, hdcMonitor, lprcMonitor, dwData) =>
{
var monitorInfo = new MONITORINFOEX { cbSize = size };
if (GetMonitorInfo(monitorHandle, ref monitorInfo))
{
list.Add((monitorInfo.szDevice, monitorHandle));
}
return true;
}, IntPtr.Zero);
return list.ToArray();
}
private static string ConvertToDeviceInstanceId(string? deviceId)
{
if (!string.IsNullOrEmpty(deviceId))
{
var pattern = new Regex(@"\\\?\\DISPLAY#(?<hardware>\w+)#(?<instance>[\w|&]+)#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}");
var match = pattern.Match(deviceId);
if (match.Success)
{
return $@"DISPLAY\{match.Groups["hardware"]}\{match.Groups["instance"]}";
}
}
return string.Empty;
}
[DllImport("User32.dll", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumDisplayDevices(
string? lpDevice,
uint iDevNum,
ref DISPLAY_DEVICE lpDisplayDevice,
uint dwFlags);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct DISPLAY_DEVICE
{
public uint cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public uint StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
}
private const uint EDD_GET_DEVICE_INTERFACE_NAME = 0x00000001;
[DllImport("User32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumDisplayMonitors(
IntPtr hdc,
IntPtr lprcClip,
MonitorEnumProc lpfnEnum,
IntPtr dwData);
[return: MarshalAs(UnmanagedType.Bool)]
private delegate bool MonitorEnumProc(
IntPtr hMonitor,
IntPtr hdcMonitor,
IntPtr lprcMonitor,
IntPtr dwData);
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetMonitorInfo(
IntPtr hMonitor,
ref MONITORINFOEX lpmi);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct MONITORINFOEX
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string szDevice;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
}