Reputation: 77
My main development machine is a laptop with 2 screens: an internal screen and an external Samsung monitor.
Generic PnP Monitor= 1366x768, Top: 0, Left: 1920 -> secondary display
SF350_S24F350FH / S24F352FH / S24F354FH (HDMI)= 1920x1080, Top: 0, Left: 0 -> main display
And my codes are: Dispay.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
internal class Display
{
private Rectangle _bounds;
private DisplayOrientation _orientation;
private Rectangle _workingArea;
private string _name,_deviceId;
private static Display[] _displays;
public Rectangle Bounds
{
get
{
return _bounds;
}
}
public DisplayOrientation Orientation
{
get
{
return _orientation;
}
}
public Rectangle WorkingArea
{
get
{
return _workingArea;
}
}
public string DeviceId
{
get
{
return _deviceId;
}
}
public string Name
{
get
{
return _name;
}
}
public static DisplayImpl[] Displays
{
get
{
if (_displays == null) QueryDisplayDevices();
return _displays;
}
private static void QueryDisplayDevices()
{
List<Display> list = new List<Display>();
WinApi.MonitorEnumDelegate MonitorEnumProc = new WinApi.MonitorEnumDelegate((IntPtr hMonitor, IntPtr hdcMonitor, ref WinApi.RECT lprcMonitor, IntPtr dwData) => {
WinApi.MONITORINFOEX mi = new WinApi.MONITORINFOEX() { Size = Marshal.SizeOf(typeof(WinApi.MONITORINFOEX)) };
if (WinApi.GetMonitorInfo(hMonitor, ref mi))
{
WinApi.DISPLAY_DEVICE device = new WinApi.DISPLAY_DEVICE();
device.Initialize();
if (WinApi.EnumDisplayDevices(mi.DeviceName.ToLPTStr(), 0, ref device, 0))
{
Display display = new Display()
{
_name = device.DeviceString,
_deviceId = mi.DeviceName,
_bounds=new Rectangle(mi.Monitor.Left,mi.Monitor.Top,mi.Monitor.Right-mi.Monitor.Left,mi.Monitor.Bottom-mi.Monitor.Top),
_workingArea = new Rectangle(mi.WorkArea.Left, mi.WorkArea.Top, mi.WorkArea.Right - mi.WorkArea.Left, mi.WorkArea.Bottom - mi.WorkArea.Top),
};
list.Add(display);
}
}
return true;
});
WinApi.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnumProc, IntPtr.Zero);
_displays=list.ToArray();
}
}
}
WinApi.cs:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
internal class WinApi
{
#region DISPLAY_DEVICE struct
[StructLayout(LayoutKind.Sequential)]
internal struct DISPLAY_DEVICE
{
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public DisplayDeviceStateFlags StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
public void Initialize()
{
cb = 0;
DeviceName = new string((char)32, 32);
DeviceString = new string((char)32, 128);
DeviceID = new string((char)32, 128);
DeviceKey = new string((char)32, 128);
cb = Marshal.SizeOf(this);
}
}
#endregion
#region RECT struct
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
#endregion
#region MONITORINFOEX struct
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MONITORINFOEX
{
public int Size;
public RECT Monitor;
public RECT WorkArea;
public uint Flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
}
#endregion
#region DisplayDeviceStateFlags enum
[Flags()]
public enum DisplayDeviceStateFlags : int
{
/// <summary>The device is part of the desktop.</summary>
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
/// <summary>The device is part of the desktop.</summary>
PrimaryDevice = 0x4,
/// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
MirroringDriver = 0x8,
/// <summary>The device is VGA compatible.</summary>
VGACompatible = 0x10,
/// <summary>The device is removable; it cannot be the primary display.</summary>
Removable = 0x20,
/// <summary>The device has more display modes than its output devices support.</summary>
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000,
}
#endregion
public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
[DllImport("user32.dll")]
public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi);
[DllImport("User32.dll")]
internal static extern bool EnumDisplayDevices(byte[] lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);
public static byte[] ToLPTStr(this string str)
{
var lptArray = new byte[str.Length + 1];
var index = 0;
foreach (char c in str.ToCharArray())
lptArray[index++] = Convert.ToByte(c);
lptArray[index] = Convert.ToByte('\0');
return lptArray;
}
}
Then I try to debug
if(Display.Displays !=null) { }
And I got these results:
Display 0:
Name: Generic PnP Monitor
DeviceId: \\\\.\\DISPLAY1
Bounds:
Top: 0
Left: 0
Width: 1920
Height: 1080
Display 1:
Name: SF350_S24F350FH / S24F352FH / S24F354FH (HDMI)
DeviceId: \\\\.\\DISPLAY2
Bounds:
Top: 0
Left: 1920
Width: 1366
Height: 768
Based on screen resolution and top-left values, Display 0 should be "SF350_S24F350FH / S24F352FH / S24F354FH (HDMI)", and why it got swapped with Display 1?
Upvotes: 1
Views: 2513
Reputation: 77
With @Dmo 's clues, I use this library
Rectangle rect;
Display display;
foreach (PathInfo pi in PathInfo.GetActivePaths())
{
if (!pi.TargetsInfo[0].DisplayTarget.IsAvailable) continue;
rect=System.Windows.Forms.Screen.GetWorkingArea(new Rectangle(pi.Position, pi.Resolution));
display = new DisplayImpl()
{
_name = string.IsNullOrEmpty(pi.TargetsInfo[0].DisplayTarget.FriendlyName)? "Generic PnP Monitor" : pi.TargetsInfo[0].DisplayTarget.FriendlyName,
_deviceId = pi.DisplaySource.DisplayName,
_devicePath=pi.TargetsInfo[0].DisplayTarget.DevicePath,
_bounds = new Rectangle(pi.Position,pi.Resolution),
_workingArea = rect,
};
list.Add(display);
}
It produces the correct monitor name and settings pair! 👍
Upvotes: 0
Reputation: 7170
According to the document of the EnumDisplayDevices
To obtain information on a display monitor, first call
EnumDisplayDevices
with lpDevice set toNULL
. Then callEnumDisplayDevices
withlpDevice
set toDISPLAY_DEVICE.DeviceName
from the first call toEnumDisplayDevices
and withiDevNum
set tozero
. ThenDISPLAY_DEVICE.DeviceString
is the monitor name.
The samples:
private static void QueryDisplayDevices()
{
DISPLAY_DEVICE device = new DISPLAY_DEVICE();
device.Initialize();
uint DispNum = 0;
while (EnumDisplayDevices(null, DispNum, ref device, 0))
{
DISPLAY_DEVICE dev = new DISPLAY_DEVICE();
dev.Initialize();
if (EnumDisplayDevices(device.DeviceName, 0, ref dev, 0))
{
Console.WriteLine("Device Name:" + dev.DeviceName);
Console.WriteLine("Monitor name:" + dev.DeviceString);
}
DispNum++;
device.Initialize();
}
}
Upvotes: 0
Reputation: 161
It depends on which monitor is defined as the main one. It does not matter the position of each monitor.
This library does the job very well if you want : WindowsDisplayAPI
Upvotes: 1