I am working on fullscreen application and I want to add funcitonality to select monitor which should be used to display applicaiton so you can swap from primary to other monitor. I checked some other applications which allow user to select display output for fullscreen or monitor for screen sharing. All of them use different naming for outputs.
Some examples:
- Skype: All my monitors are called "Generic PnP Monitor"
- Discord: Screen 1, Screen 2
- World Of Warcraft: Primary, Monitor 1, Monitor 2
- Advanced Display Settings (Windows): Display 1: Benq..., Display 2: T24i...
I already know that I can get DisplayArea using:
var areas = DisplayArea.FindAll();
Then I can iterate areas and get display name like this:
var area0 = areas[0];
var monitorHw = Microsoft.UI.Win32Interop.GetMonitorFromDisplayId(area0.DisplayId);
PInvoke.User32.GetMonitorInfo(monitorHw, out var monitorInfo1);
var monitorName1 = new string(monitorInfo1.DeviceName); // monitorName1 = \\.\DISPLAY1
I know that DisplayArea has static getter for DisplayArea.Primary. those two things together might explain settings in WoW graphics where they put single primary by default and just rename MONITORINFOEX.DeviceName ot Monitor . Similar approach could be used in Discord.
BUT SKYPE...AND...WINDOWS SETTINGS...
I know that I can get "quite" nice display names like this (source):
public static unsafe void GetDisplayNames()
{
DISPLAY_DEVICE lpDisplayDevice = new();
lpDisplayDevice.cb = (uint) Marshal.SizeOf(lpDisplayDevice);
DISPLAY_DEVICE monitor_name = new();
monitor_name.cb = (uint) Marshal.SizeOf(monitor_name);
uint devNum = 0;
var msg = "";
while (PInvoke.User32.EnumDisplayDevices(null, devNum, ref lpDisplayDevice, 0))
{
msg += "DeviceName =" + new string(lpDisplayDevice.DeviceName).Trim() + '\n';
PInvoke.User32.EnumDisplayDevices(new string(lpDisplayDevice.DeviceName), 0, ref monitor_name, 0);
msg += "Monitor name =" + new string(monitor_name.DeviceString).Trim() + '\n';
++devNum;
}
Console.WriteLine(msg);
}
Best display names I get when I use this code:
var displayMonitorSelector = DisplayMonitor.GetDeviceSelector();
var displayMonitorDeviceInformation = (await DeviceInformation.FindAllAsync(displayMonitorSelector))[0];
DisplayMonitor? displayMonitor = await DisplayMonitor.FromInterfaceIdAsync(displayMonitorDeviceInformation.Id);
var displayMonitorName = displayMonitor.DisplayName; // BenQ EX3203R
// OR (Not sure which works better for me yet)
var projectionSelector = ProjectionManager.GetDeviceSelector();
var projectionDeviceInformation = (await DeviceInformation.FindAllAsync(projectionSelector))[0];
DisplayMonitor? projection = await DisplayMonitor.FromInterfaceIdAsync(projectionDeviceInformation.Id);
var projectionName = projection.DisplayName; // BenQ EX3203R
But I can not get any information about WorkArea and Bounds in last two approaches. WorkArea and Bounds seems to be key to move my app window to correct screen.
Possible solution
I came with this idea: All three outputs seems to be sorted same way. Monitor 1 has always area 1, monitor 2 has area 2.
Question
Can I rely on this ordering behavior? Is there better approach to get display name, it's bounds and work area (also DPI would be great but I have a working function for this)?