Windows中EnumDisplayDevices不返回非活动显示器,如何检测失效显示器?
解决非活动显示器检测问题
方法一:修改EnumDisplayDevices的调用参数
EnumDisplayDevices默认仅返回活动状态的显示设备,要获取所有设备(包括非活动的),需在调用时传入EDD_ALL_DEVICES(值为0x00000002)作为第四个参数。
修改你的代码如下:
const uint EDD_ALL_DEVICES = 0x00000002; const uint DISPLAY_DEVICE_ACTIVE = 0x00000001; DISPLAY_DEVICEW adapter = new DISPLAY_DEVICEW(); adapter.cb = Marshal.SizeOf(adapter); uint deviceNum = 0; while (EnumDisplayDevicesW(null, deviceNum, ref adapter, EDD_ALL_DEVICES)) { Console.WriteLine("适配器名称: " + adapter.DeviceName); Console.WriteLine("设备状态: " + ((adapter.StateFlags & DISPLAY_DEVICE_ACTIVE) != 0 ? "活动" : "非活动")); DISPLAY_DEVICEW display = new DISPLAY_DEVICEW(); display.cb = Marshal.SizeOf(display); uint monitorNum = 0; while (EnumDisplayDevicesW(adapter.DeviceName, monitorNum, ref display, EDD_ALL_DEVICES)) { Console.WriteLine("显示器名称: " + display.DeviceName); Console.WriteLine("显示器状态: " + ((display.StateFlags & DISPLAY_DEVICE_ACTIVE) != 0 ? "活动" : "非活动")); monitorNum++; } deviceNum++; Console.WriteLine("-------------------------------------"); }
方法二:使用SetupAPI枚举所有显示设备
对于USB集线器连接的显示器,部分非活动设备可能无法通过EnumDisplayDevices枚举到,这时可以用SetupAPI直接枚举系统中所有显示类设备,不受驱动激活状态影响。
示例代码:
using System; using System.Runtime.InteropServices; public class SetupApiDisplayEnumerator { private const uint DIGCF_PRESENT = 0x00000002; private const uint DIGCF_DEVICEINTERFACE = 0x00000010; private const int SPDRP_FRIENDLYNAME = 0x0000000C; [StructLayout(LayoutKind.Sequential)] private struct SP_DEVICE_INTERFACE_DATA { public int cbSize; public Guid interfaceClassGuid; public int flags; public IntPtr reserved; } [StructLayout(LayoutKind.Sequential)] private struct SP_DEVINFO_DATA { public int cbSize; public Guid classGuid; public uint devInst; public IntPtr reserved; } [DllImport("setupapi.dll", SetLastError = true)] private static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid, IntPtr enumerator, IntPtr hwndParent, uint flags); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfoData, ref Guid interfaceClassGuid, uint memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr hDevInfo, ref SP_DEVINFO_DATA devInfoData, uint property, out uint propertyRegDataType, IntPtr propertyBuffer, uint propertyBufferSize, out uint requiredSize); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, out uint requiredSize, ref SP_DEVINFO_DATA devInfoData); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiDestroyDeviceInfoList(IntPtr hDevInfo); private static readonly Guid GUID_DEVINTERFACE_MONITOR = new Guid("{E6F07B5F-EE97-4a90-B076-33F57BF4EAA7}"); public static void EnumerateAllMonitors() { IntPtr hDevInfo = SetupDiGetClassDevs(ref GUID_DEVINTERFACE_MONITOR, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDevInfo == IntPtr.Zero) { Console.WriteLine("获取设备列表失败"); return; } try { SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); uint index = 0; while (SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref GUID_DEVINTERFACE_MONITOR, index, ref deviceInterfaceData)) { SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA(); devInfoData.cbSize = Marshal.SizeOf(devInfoData); uint requiredSize = 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, ref deviceInterfaceData, IntPtr.Zero, 0, out requiredSize, ref devInfoData); IntPtr detailDataPtr = Marshal.AllocHGlobal((int)requiredSize); Marshal.WriteInt32(detailDataPtr, (IntPtr.Size == 4) ? 4 + Marshal.SystemDefaultCharSize : 8); if (SetupDiGetDeviceInterfaceDetail(hDevInfo, ref deviceInterfaceData, detailDataPtr, requiredSize, out requiredSize, ref devInfoData)) { string devicePath = Marshal.PtrToStringUni(IntPtr.Add(detailDataPtr, (IntPtr.Size == 4) ? 4 : 8)); Console.WriteLine("设备路径: " + devicePath); uint regDataType; uint bufferSize = 256; IntPtr friendlyNameBuffer = Marshal.AllocHGlobal((int)bufferSize); if (SetupDiGetDeviceRegistryProperty(hDevInfo, ref devInfoData, SPDRP_FRIENDLYNAME, out regDataType, friendlyNameBuffer, bufferSize, out requiredSize)) { string friendlyName = Marshal.PtrToStringUni(friendlyNameBuffer); Console.WriteLine("显示器名称: " + friendlyName); } Marshal.FreeHGlobal(friendlyNameBuffer); } Marshal.FreeHGlobal(detailDataPtr); index++; Console.WriteLine("-------------------------------------"); } } finally { SetupDiDestroyDeviceInfoList(hDevInfo); } } }
调用方式:
SetupApiDisplayEnumerator.EnumerateAllMonitors();
关键说明
EDD_ALL_DEVICES标志是让EnumDisplayDevices返回所有设备的核心,默认传入0仅返回活动设备- USB显示器属于复合硬件设备,SetupAPI能从底层枚举所有连接的显示器,不受显示驱动激活状态限制
内容的提问来源于stack exchange,提问作者Abhijit Borkakoty
相关产品推荐
相关产品推荐

