0

For my program I need to get detailed information about the current displays. In my research I came across this post with talks about linking the System.Windows.Forms.Screen class and its EDID information. At first I tried copying and pasting the code found with using p/invoke to supply all the required native methods and structs, but it did not work and only gave me a string of ? for the InstanceID. So instead I tried to use the MSDN resources and again p/invoke to create the code myself. This is what I came up with:

private static void Foo()
{
    Guid DisplayGUID = new Guid(Bar.GUID_DEVINTERFACE_MONITOR);

    IntPtr DisplaysHandle = Bar.SetupDiGetClassDevs(ref DisplayGUID, null, IntPtr.Zero, (uint)(Win32.DIGCF_PRESENT | Win32.DIGCF_DEVICEINTERFACE));

    Bar.SP_DEVICE_INTERFACE_DATA Data = new Bar.SP_DEVICE_INTERFACE_DATA();
    Data.cbSize = Marshal.SizeOf(Data);

    for (uint id = 0; Bar.SetupDiEnumDeviceInterfaces(DisplaysHandle, IntPtr.Zero, ref DisplayGUID, id, ref Data); id++)
    {
        Bar.SP_DEVINFO_DATA SPDID = new Bar.SP_DEVINFO_DATA();
        SPDID.cbSize = (uint)Marshal.SizeOf(SPDID);

        Bar.SP_DEVICE_INTERFACE_DETAIL_DATA NDIDD = new Bar.SP_DEVICE_INTERFACE_DETAIL_DATA();

        if (IntPtr.Size == 8) //64 bit
            NDIDD.cbSize = 8;
        else //32 bit
            NDIDD.cbSize = 4 + Marshal.SystemDefaultCharSize;

        uint requiredsize = 0;
        uint buffer = Bar.BUFFER_SIZE;

        if (Bar.SetupDiGetDeviceInterfaceDetail(DisplaysHandle, ref Data, ref NDIDD, buffer, ref requiredsize, ref SPDID))
        {
            uint size = 0;
            Bar.CM_Get_Device_ID_Size(out size, SPDID.DevInst);

            IntPtr ptrInstanceBuf = Marshal.AllocHGlobal((int)size);

            Bar.CM_Get_Device_ID(SPDID.DevInst, ref ptrInstanceBuf, size);

            string InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf);

            Console.WriteLine("InstanceID: {0}", InstanceID);

            Marshal.FreeHGlobal(ptrInstanceBuf);

            Console.WriteLine("DevicePath: {0}\n", NDIDD.DevicePath);
        }
    }

    Bar.SetupDiDestroyDeviceInfoList(DisplaysHandle);
}

private class Bar
{
    [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, [MarshalAs(UnmanagedType.LPTStr)] string Enumerator, IntPtr hwndParent, uint Flags);

    [DllImport("setupapi.dll", SetLastError = true)]
    public static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

    [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

    [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, UInt32 deviceInterfaceDetailDataSize, ref UInt32 requiredSize, ref SP_DEVINFO_DATA deviceInfoData);

    [DllImport("setupapi.dll", SetLastError = true)]
    public static extern int CM_Get_Device_ID_Size(out uint pulLen, UInt32 dnDevInst, int flags = 0);

    [DllImport("setupapi.dll", SetLastError = true)]
    public static extern int CM_Get_Device_ID(uint dnDevInst, ref IntPtr Buffer, uint BufferLen, int ulFlags = 0);

    public const int BUFFER_SIZE = 168; //guess

    public const string GUID_DEVINTERFACE_MONITOR = "{E6F07B5F-EE97-4a90-B076-33F57BF4EAA7}";

    [Flags]
    public enum DiGetClassFlags : uint
    {
        DIGCF_DEFAULT = 0x00000001,  // only valid with DIGCF_DEVICEINTERFACE
        DIGCF_PRESENT = 0x00000002,
        DIGCF_ALLCLASSES = 0x00000004,
        DIGCF_PROFILE = 0x00000008,
        DIGCF_DEVICEINTERFACE = 0x00000010,
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SP_DEVICE_INTERFACE_DATA
    {
        public Int32 cbSize;
        public Guid interfaceClassGuid;
        public Int32 flags;
        private UIntPtr reserved;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SP_DEVINFO_DATA
    {
        public uint cbSize;
        public Guid classGuid;
        public uint DevInst;
        public IntPtr reserved;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct SP_DEVICE_INTERFACE_DETAIL_DATA
    {
        public int cbSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)]
        public string DevicePath;
    }
}

My code compiles and runs, but it does not give me the output I am looking for.

The output that I am looking for is:

InstanceID: DISPLAY\DELA00B\5&786e6ca&0&UID1048832
DevicePath: \\?\display#dela00b#5&786e6ca&0&uid1048832#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}

But this is the output I am receiving:

InstanceID: l
DevicePath: \\?\display#dela00b#5&786e6ca&0&uid1048832#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}

My question comes in the form of what is the problem causing the InstanceID to not output correctly.

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137

1 Answers1

1

Turns out I was using the wrong p/invoke signature. CM_Get_Device_ID should look like this:

[DllImport("setupapi.dll", SetLastError = true)]
public static extern int CM_Get_Device_ID(uint dnDevInst, StringBuilder Buffer, int BufferLen, int ulFlags = 0);

Also Updated Usage Code:

StringBuilder IDBuffer = new StringBuilder((int)buffer);
Bar.CM_Get_Device_ID(SPDID.DevInst, IDBuffer, (int)buffer);

Console.WriteLine("InstanceID: {0}", IDBuffer.ToString());
Console.WriteLine("DevicePath: {0}\n", NDIDD.DevicePath);