3

I am having an issue working with EnumDisplayDevices in C#. I used the code posted here and it worked seamlessly. However recently I switched to coding the application on my desktop computer. The code no longer works, not giving me anything.

If it helps, my monitor is an LG W2753VC running on an AMD Radeon HD 5770.

Here is my current code:

        var device = new DISPLAY_DEVICE();

        device.cb = Marshal.SizeOf(device);
        try {
            for (uint id = 0; EnumDisplayDevices(null, id, ref device, 0); id++) {
                device.cb = Marshal.SizeOf(device);
                EnumDisplayDevices(device.DeviceName, 0, ref device, 0);
                device.cb = Marshal.SizeOf(device);

                Console.WriteLine("id={0}, name={1}, devicestring={2}", id, device.DeviceName, device.DeviceString);
                if (device.DeviceName == null || device.DeviceName == "") continue;
            }
        } catch (Exception ex) { }

What I'm getting on my laptop:

id=0, name=\\.\DISPLAY1\Monitor0, devicestring=Generic PnP Monitor

(That should be "Mobile PC Display", but it's not the problem here)

What I'm getting on my desktop:

id=0, name=, devicestring=
id=1, name=, devicestring=

Am I doing this wrong, and if not, is there any other way to get all connected displays? I have already tried the WMI and registry approach.

Community
  • 1
  • 1
pbondoer
  • 538
  • 7
  • 15

4 Answers4

12

Hard to tell without you posting all your p/invoke code. Here's code that works on my workstation:

public class EnumDisplayDevicesTest
{
    [DllImport("user32.dll")]
    static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);

    [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
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct DISPLAY_DEVICE
    {
        [MarshalAs(UnmanagedType.U4)]
        public int cb;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DeviceName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceString;
        [MarshalAs(UnmanagedType.U4)]
        public DisplayDeviceStateFlags StateFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceKey;
    }

    public void Display()
    {
        DISPLAY_DEVICE d = new DISPLAY_DEVICE();
        d.cb = Marshal.SizeOf(d);
        try
        {
            for (uint id = 0; EnumDisplayDevices(null, id, ref d, 0); id++)
            {
                if (d.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop))
                {
                    Console.WriteLine(
                        String.Format("{0}, {1}, {2}, {3}, {4}, {5}",
                                 id,
                                 d.DeviceName,
                                 d.DeviceString,
                                 d.StateFlags,
                                 d.DeviceID,
                                 d.DeviceKey
                                 )
                                 );
                    d.cb = Marshal.SizeOf(d);
                    EnumDisplayDevices(d.DeviceName, 0, ref d, 0);
                    Console.WriteLine(
                        String.Format("{0}, {1}",
                                 d.DeviceName,
                                 d.DeviceString
                                 )
                                 );
                }
                d.cb = Marshal.SizeOf(d);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(String.Format("{0}", ex.ToString()));
        }
    }
}

The above code works as the OP desires on my machines, not sure why it won't work on his laptop.

An alternative method to getting the info is listed below, taken from here:

foreach (var display in DisplayDetails.GetMonitorDetails())
{
    Console.WriteLine(display.Model);
}

public class DisplayDetails
{
    public string PnPID { get; set; }

    public string SerialNumber { get; set; }

    public string Model { get; set; }

    public string MonitorID { get; set; }

    /// <summary>
    /// The Constructor to create a new instances of the DisplayDetails class...
    /// </summary>
    public DisplayDetails(string sPnPID, string sSerialNumber, string sModel, string sMonitorID)
    {
        PnPID = sPnPID;
        SerialNumber = sSerialNumber;
        Model = sModel;
        MonitorID = sMonitorID;
    }

    /// <summary>
    /// This Function returns all Monitor Details
    /// </summary>
    /// <returns></returns>
    static public IEnumerable<DisplayDetails> GetMonitorDetails()
    {
        //Open the Display Reg-Key
        RegistryKey Display = Registry.LocalMachine;
        Boolean bFailed = false;
        try
        {
            Display = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Enum\DISPLAY");
        }
        catch
        {
            bFailed = true;
        }

        if (!bFailed & (Display != null))
        {

            //Get all MonitorIDss
            foreach (string sMonitorID in Display.GetSubKeyNames())
            {
                RegistryKey MonitorID = Display.OpenSubKey(sMonitorID);

                if (MonitorID != null)
                {
                    //Get all Plug&Play ID's
                    foreach (string sPNPID in MonitorID.GetSubKeyNames())
                    {
                        RegistryKey PnPID = MonitorID.OpenSubKey(sPNPID);
                        if (PnPID != null)
                        {
                            string[] sSubkeys = PnPID.GetSubKeyNames();

                            //Check if Monitor is active
                            if (sSubkeys.Contains("Control"))
                            {
                                if (sSubkeys.Contains("Device Parameters"))
                                {
                                    RegistryKey DevParam = PnPID.OpenSubKey("Device Parameters");
                                    string sSerial = "";
                                    string sModel = "";

                                    //Define Search Keys
                                    string sSerFind = new string(new char[] { (char)00, (char)00, (char)00, (char)0xff });
                                    string sModFind = new string(new char[] { (char)00, (char)00, (char)00, (char)0xfc });

                                    //Get the EDID code
                                    byte[] bObj = DevParam.GetValue("EDID", null) as byte[];
                                    if (bObj != null)
                                    {
                                        //Get the 4 Vesa descriptor blocks
                                        string[] sDescriptor = new string[4];
                                        sDescriptor[0] = Encoding.Default.GetString(bObj, 0x36, 18);
                                        sDescriptor[1] = Encoding.Default.GetString(bObj, 0x48, 18);
                                        sDescriptor[2] = Encoding.Default.GetString(bObj, 0x5A, 18);
                                        sDescriptor[3] = Encoding.Default.GetString(bObj, 0x6C, 18);

                                        //Search the Keys
                                        foreach (string sDesc in sDescriptor)
                                        {
                                            if (sDesc.Contains(sSerFind))
                                            {
                                                sSerial = sDesc.Substring(4).Replace("\0", "").Trim();
                                            }
                                            if (sDesc.Contains(sModFind))
                                            {
                                                sModel = sDesc.Substring(4).Replace("\0", "").Trim();
                                            }
                                        }


                                    }
                                    if (!string.IsNullOrEmpty(sPNPID + sSerFind + sModel + sMonitorID))
                                    {
                                        yield return new DisplayDetails(sPNPID, sSerial, sModel, sMonitorID);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
GalacticJello
  • 11,235
  • 2
  • 25
  • 35
  • This seems to get the graphics cards attached to my computer. How do I get the monitors out of that? I tried performing a second call to `EnumDisplayDevices` but the same problem persists. – pbondoer Sep 16 '13 at 20:19
  • 1
    Make sure you are only looking at devices that are attached to the desktop (DisplayDeviceStateFlags.AttachedToDesktop). See edited code above. – GalacticJello Sep 17 '13 at 13:11
  • Still no good. It first displays the graphics card (as expected) and then displays `, `. It seems all strings are empty in the object :( – pbondoer Sep 17 '13 at 15:32
  • I added an alternative method to getting the info, if that works for you. – GalacticJello Sep 18 '13 at 18:29
  • I have already tested this approach and it did not work. It works on Windows 7 but not on Windows 8 :( – pbondoer Sep 21 '13 at 13:34
  • The EnumDisplayDevices solution works fine on Windows 10 - also for getting the monitor devices. – blanne Apr 03 '19 at 11:33
2

I had exactly this behavior when device.cb wasn't equal to one of 840 or 224 (sizeof DISPLAY_DEVICEW and DISPLAY_DEVICEA respectively). In my case I just mistakenly wrote device.cb = sizeof(device.cb); Check value of Marshal.SizeOf(device) on both your computers.

Taher Rahgooy
  • 6,528
  • 3
  • 19
  • 30
1

I had the exact same problem and I found out that I was using the following to import EnumDisplayDevices:

[DllImport("user32.dll", CharSet = CharSet.Auto)] 

Rather than

[DllImport("user32.dll")]
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
OscarBoo
  • 11
  • 1
0

The code example from GalacticJello is working for me. I forced the build of the assemblies for x86.

In other cases maybe try: [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct DISPLAY_DEVICE`

but this is not working for x86!

Trivalik
  • 80
  • 7