寻找一个可靠的Forms.Screen.DeviceName映射到监视EDID信息

我正在开发一个应用程序,该应用程序将在相应的显示器上的对话框中显示来自EDID块(监视器型号,ID,S / N等)的信息。

此代码适用于查找显示器的EDID信息。 它通过枚举HKLM \ SYSTEM \ CurrentControlSet \ Enum \ DISPLAY \ [Monitor] \ [PnPID] \ Device Parameters \ EDID下的DISPLAY键来提取EDID信息。

更新:上面的代码是依靠PnP使用registry的“副作用”。 我现在正在使用SetupAPI枚举监视器,它可以正确处理监视器的连接/删除(不像上面链接的代码)。

我试图关联Windows.Forms.Screen.AllScreens [](\\。\ DISPLAY1,\\。\ DISPLAY2等)中的每个屏幕与从上面的registry检查返回的条目。

注意:在下面的代码块中,DisplayDetails.GetMonitorDetails()现在已经被使用SetupAPI的更健壮的registry枚举代码replace,但返回的数据是相同的。

例如

private void Form1_Load(object sender, EventArgs e) { Console.WriteLine("Polling displays on {0}:", System.Environment.MachineName); int i = 0; foreach ( DisplayDetails dd in DisplayDetails.GetMonitorDetails()) { Console.WriteLine( "Info: Model: {0}, MonitorID: {1}, PnPID: {2}, Serial#:{3}", dd.Model, dd.MonitorID, dd.PnPID, dd.SerialNumber ); Console.WriteLine( "Does this correlate to Screen: {0}?", Screen.AllScreens[i++].DeviceName ); } } 

输出:

信息:型号:DELL P2411H,MonitorID:DELA06E,PnPID:5&2e2fefea&0&UID1078018,序列号:F8NDP0C … PU

这是否与屏幕相关:\\。\ DISPLAY1?

信息:型号:DELL P2411H,MonitorID:DELA06E,PnPID:5&2e2fefea&0&UID1078019,序列号:F8NDP0C … AU

这是否与屏幕相关:\\。\ DISPLAY2?


答:没有

在testing中,我发现这些不能可靠地关联(我有一个系统,第一个显示枚举是\\。\ DISPLAY2)。

我的问题: 有没有办法可靠地得到给定的Forms.Screen的EDID信息? 我可以得到的EDID块,但没有findpath来关联这个UI的顶级窗体。 提示用户是不可取的,因为在我的使用情况下,两个(或更多)显示器可能是相同的型号和分辨率,并且仅在S / N上相差几个数字。

我已经在Forms.Screen API,Win32 EnumDisplay,其他registryGUID(即插即用和驱动程序相关)之后寻找path,但没有find任何有希望的path。

我也调查了WMI Win32_DesktopMonitor API(Windows 7),但它似乎没有任何更多的信息可以帮助我将它关联到Windows.Forms.Screen.AllScreens []条目。

我怀疑是否有办法做到这一点,它是通过SetupAPI,但我还没有find它。

EnumDisplayDevices API中提供了解决GDI到SetupAPI的方法。 如果您通过EDD_GET_DEVICE_INTERFACE_NAME为dwFlags,则监视器枚举将返回以下形式的DeviceID信息:

 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} 

DeviceID字段现在与didd.DevicePath的结果相匹配,如下面的C#代码片段所示:

  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); } 

示例输出:

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

原始EnumDisplayDevices中的DeviceName与Forms.Screen.DeviceName属性匹配。

有了这两条信息,现在可以在SetupDIEnumDeviceInterface遍历期间使用如下所示的片段读取EDID块:

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

最后,可以针对VESA描述符块进行解析,如代码中的DisplayDetails.GetMonitorDetails()方法所示。