Header

Monitor Detection

2019-07-24 by cYo

Detecting connected Monitors, their names, their resolution, their sizes and their relative position is a non trivial process in Windows. You basically make a trip through the history of Windows APIs, all their different incarnations and limitations. And don’t forget the crazy DPI awareness for Windows apps that is by my counting now in the 4th iteration.

Actually Connected Monitors and their internal Device Names

Best way is to call EnumDisplayDevices with the AttachedToDesktop enum.

var ddl = new List<DisplayDevice>();

DisplayDevice d = new DisplayDevice();
d.cb = Marshal.SizeOf(d);

for (uint id = 0; EnumDisplayDevices(null, id, ref d, 0); id++)
{
    if (!d.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop))
        continue;

    var d2 = new DisplayDevice();
    d2.cb = Marshal.SizeOf(d2);
    EnumDisplayDevices(d.DeviceName, 0, ref d2, 0);
    d2.DeviceName = d.DeviceName;
    ddl.Add(d2);
}

Physical Sizes of Monitors

To get the physical sizes of monitors (if available) is by querying WMI. The id check in the sample code is just some internal lookup logic in Walls.

public (float width, float height) GetMonitorPhysicalSize(string id)
{
    try
    {
        var scope = new ManagementScope("\\\\.\\ROOT\\WMI");

        var q = new SelectQuery("WmiMonitorBasicDisplayParams");
        var searcher = new ManagementObjectSearcher(scope, q);
        var qc = searcher.Get();

        foreach (var mo in qc)
        {
            string mi = mo["InstanceName"].ToString();

            if (IsMonitorWmiId(id, mi))
                return (float.Parse(mo["MaxHorizontalImageSize"].ToString()), float.Parse(mo["MaxVerticalImageSize"].ToString()));
        }
    }
    catch (Exception e)
    {
        Debug.WriteLine("WMI Monitor error:" + e);
    }

    return (0, 0);
}

Name of the Monitor Model

If WMI is not available (e.g. VMWare Virtual Machines) we build a name out of the device ID.

public string GetMonitorModel(string id)
{
    try
    {
        var scope = new ManagementScope("\\\\.\\ROOT\\WMI");

        var q = new SelectQuery("WmiMonitorID");
        var searcher = new ManagementObjectSearcher(scope, q);
        var qc = searcher.Get();

        foreach (var mo in qc)
        {
            string mi = mo["InstanceName"].ToString();

            if (!IsMonitorWmiId(id, mi))
                continue;

            var nameArray = ((UInt16[])(mo["UserFriendlyName"]))
                .Select(i => (byte)i).Where(n => n != 0).ToArray();
            return System.Text.Encoding.UTF8.GetString(nameArray);
        }
    }
    catch (Exception e)
    {
        Debug.WriteLine("WMI Monitor error:" + e);
    }

    try
    {
        return id.Split('#')[1];
    }
    catch
    {
    }

    return null;
}

Positions and Resolutions of Monitors

The positions are defined in pixels on the Virtual Screen of all monitors combined. Note that the pixels maybe scaled if you have different DPI settings for the monitors. So it is best to switch the app to non DPI mode when getting the information. The information itself can be queried with the EnumDisplayMonitors function.

SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNAWARE);

var miexl = new List<MonitorInfoEx>();

bool callback(IntPtr hMonitor, IntPtr hdc, ref Rect prect, int dn)
{
    var info = new MonitorInfoEx();
    GetMonitorInfo(hMonitor, info);
    miexl.Add(info);
    return true;
}
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0);
Back