Looking for a reliable mapping of Forms.Screen.DeviceName to Monitor EDID info

后端 未结 1 1157
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-14 13:15

I\'m developing an application which will display information derived from the EDID blocks (monitor model, ID, S/N, etc.) on a dialog on the corresponding monitor.

T

1条回答
  •  天涯浪人
    2020-12-14 13:59

    A method to resolve the GDI to SetupAPI is available in the EnumDisplayDevices API. If you pass in the EDD_GET_DEVICE_INTERFACE_NAME in for dwFlags, the monitor enumeration will return DeviceID information of the form:

    Monitor 0 info:
    DeviceName: \\.\DISPLAY1
    MonitorInfo: Dell P2411H(Digital)
    DeviceID: \\?\DISPLAY#DELA06E#5&2e2fefea&0&UID1078018#{e6f07b5f-ee97-4a90-b076-3
    3f57bf4eaa7}
    Monitor 1 info:
    DeviceName: \\.\DISPLAY2
    MonitorInfo: Dell P2411H(Digital)
    DeviceID: \\?\DISPLAY#DELA06E#5&2e2fefea&0&UID1078019#{e6f07b5f-ee97-4a90-b076-3
    3f57bf4eaa7}
    

    The DeviceID fields now match the results from the didd.DevicePath, as retrieved in the C# fragment below:

        Guid MonitorGUID = new Guid(Win32.GUID_DEVINTERFACE_MONITOR);
    
        // We start at the "root" of the device tree and look for all
        // devices that match the interface GUID of a monitor
        IntPtr h = Win32.SetupDiGetClassDevs(ref MonitorGUID, IntPtr.Zero, IntPtr.Zero, (uint)(Win32.DIGCF_PRESENT | Win32.DIGCF_DEVICEINTERFACE));
        if (h.ToInt64() != Win32.INVALID_HANDLE_VALUE)
        {
            bool Success = true;
            uint i = 0;
            while (Success)
            {
                // create a Device Interface Data structure
                Win32.SP_DEVICE_INTERFACE_DATA dia = new Win32.SP_DEVICE_INTERFACE_DATA();
                dia.cbSize = (uint)Marshal.SizeOf(dia);
    
                // start the enumeration 
                Success = Win32.SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref MonitorGUID, i, ref dia);
                if (Success)
                {
                    // build a DevInfo Data structure
                    Win32.SP_DEVINFO_DATA da = new Win32.SP_DEVINFO_DATA();
                    da.cbSize = (uint)Marshal.SizeOf(da);
    
                    // build a Device Interface Detail Data structure
                    Win32.SP_DEVICE_INTERFACE_DETAIL_DATA didd = new Win32.SP_DEVICE_INTERFACE_DETAIL_DATA();
                    didd.cbSize = (uint)(4 + Marshal.SystemDefaultCharSize); // trust me :)
    
                    // now we can get some more detailed information
                    uint nRequiredSize = 0;
                    uint nBytes = Win32.BUFFER_SIZE;
                    if (Win32.SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, nBytes, out nRequiredSize, ref da))
                    {
                        // Now we get the InstanceID
                        IntPtr ptrInstanceBuf = Marshal.AllocHGlobal((int)nBytes);
                        Win32.CM_Get_Device_ID(da.DevInst, ptrInstanceBuf, (int)nBytes, 0);
                        string InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf);
                        Console.WriteLine("InstanceID: {0}", InstanceID );
                        Marshal.FreeHGlobal(ptrInstanceBuf);
                       
                        Console.WriteLine("DevicePath: {0}", didd.DevicePath );
                    }
                    i++;
                }
            }
        }
        Win32.SetupDiDestroyDeviceInfoList(h);
    }
    

    Sample Output:

    InstanceID: DISPLAY\DELA06E\5&2E2FEFEA&0&UID1078018
    DevicePath: \\?\display#dela06e#5&2e2fefea&0&uid1078018#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
    

    The DeviceName from the original EnumDisplayDevices matches the Forms.Screen.DeviceName property.

    With these two pieces of information, it is now possible to read the EDID block during the SetupDIEnumDeviceInterface traversal using a fragment like the below:

    private static byte[] GetMonitorEDID(IntPtr pDevInfoSet, SP_DEVINFO_DATA deviceInfoData)
    {
        IntPtr hDeviceRegistryKey = SetupDiOpenDevRegKey(pDevInfoSet, ref deviceInfoData,
            DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
        if (hDeviceRegistryKey == IntPtr.Zero)
        {
            throw new Exception("Failed to open a registry key for device-specific configuration information");
        }
    
        IntPtr ptrBuff = Marshal.AllocHGlobal((int)256);
        try
        {
            RegistryValueKind lpRegKeyType = RegistryValueKind.Binary;
            int length = 256;
            uint result = RegQueryValueEx(hDeviceRegistryKey, "EDID", 0, ref lpRegKeyType, ptrBuff, ref length);
            if (result != 0)
            {
                throw new Exception("Can not read registry value EDID for device " + deviceInfoData.ClassGuid);
            }
        }
        finally
        {
            RegCloseKey(hDeviceRegistryKey);
        }
        byte[] edidBlock = new byte[256];
        Marshal.Copy(ptrBuff, edidBlock, 0, 256);
        Marshal.FreeHGlobal(ptrBuff);
        return edidBlock;
    }
    

    Which, finally, can be parsed for the VESA descriptor blocks, as shown in the DisplayDetails.GetMonitorDetails() method in this code.

    0 讨论(0)
提交回复
热议问题