C# 获取windows系统的显示器个数

缘由

最近有个工装的项目,测试HDMI接口可不可用,采取了诱骗器的方式,软件需要读到当前是否有外接屏幕的插入,最简单的就是来获取屏幕的数量,从而判断是否有外接屏幕的介入。因此有了需要查询Windows系统显示器个数的需求。

尝试过的方法

方法1

Screen.AllScreens.Count();

这个方法是利用WinForm中的Screen类,从而获得当前屏幕的数量。

方法2

using System.Management;

int number = 0;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_DesktopMonitor");
foreach (ManagementObject queryObj in searcher.Get())
{
	number++;
}

这个方法是利用WMI来查询当前系统中的显示器,得到显示信息的列表,循环得到列表的个数,从而得到显示器的个数。

方法3

[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

[DllImport("user32.dll")]
static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip,
   EnumMonitorsDelegate lpfnEnum, IntPtr dwData);

private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref ScreenRect pRect, int dwData);

int monCount = 0;

MonitorEnumProc callback = (IntPtr hDesktop, IntPtr hdc, ref ScreenRect prect, int d) => ++monCount > 0;

if (!EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0))
	Console.WriteLine("An error occured while enumerating monitors");
return monCount;

这个方法是利用Win32中的EnumDisplayMonitors方法来枚举当前系统中存在的显示器,通过回调来进行一个显示器个数的计算。

方法4

[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;
}
[DllImport("user32.dll")]
static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);

public void Display()
{
    DISPLAY_DEVICE d = new DISPLAY_DEVICE();
    d.cb = Marshal.SizeOf(d);
    int number=0;
    try
    {
        for (uint id = 0; EnumDisplayDevices(null, id, ref d, 0); id++)
        {
            if (d.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop))
            {
                //计算个数
                number++;
                //输出相关信息
                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()));
    }
}

这个方法是利用Win32中的EnumDisplayDevices方法来枚举当前系统中存在的显示器,通过循环来进行一个显示器个数的计算。

方法5

public enum SystemMetric
{
	//其他类型查看:https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics
	SM_CMONITORS=80,
	...
}

[DllImport("user32.dll")]
static extern int GetSystemMetrics(SystemMetric smIndex);

int screenNum=GetSystemMetrics(SystemMetric.SM_CMONITORS);

这个方法是利用Win32中的GetSystemMetrics方法来获取当前系统中存在的显示器个数,这个方法还可以查询系统的其他信息,详情参照官方文档。

重点来了:如果接两个显示器,设置系统处于复制模式(Clone/duplicate),则上述方法都失效,只能找到一个显示设备,但是项目正好是这个模式,哎

接下来咱们来一个不受模式影响的方法X

原理:每个种类的设备在系统中有一个类Guid,我们可以通过设备管理器中设备的属性中查看,如下图。然后我们用Win32的API枚举设备管理器中的设备,然后通过类Guid对设备进行筛选,从而筛选出屏幕设备,然后计算个数。

在这里插入图片描述

[Flags]
public enum DIGCF : uint
{
    DEFAULT = 0x00000001,
    PRESENT = 0x00000002,
    ALLCLASSES = 0x00000004,
    PROFILE = 0x00000008,
    DEVICEINTERFACE = 0x00000010
}

[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVINFO_DATA
{
    public UInt32 cbSize;
    public Guid ClassGuid;
    public UInt32 DevInst;
    public IntPtr Reserved;
}

[StructLayout(LayoutKind.Sequential)]
public struct SP_DEVINFO_DATA
{
    public UInt32 cbSize;
    public Guid ClassGuid;
    public UInt32 DevInst;
    public IntPtr Reserved;
}

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

[DllImport("setupapi.dll", SetLastError = true)]
public static extern IntPtr SetupDiGetClassDevsW([In] ref Guid ClassGuid, [MarshalAs(UnmanagedType.LPWStr)]string Enumerator, IntPtr parent, DIGCF flags);

[DllImport("setupapi.dll", SetLastError = true)]
public static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet, UInt32 memberIndex, [Out] out SP_DEVINFO_DATA deviceInfoData);

int screenNum=0;
Guid NullGuid = Guid.Empty;
IntPtr info = SetupDiGetClassDevsW(ref NullGuid, null, IntPtr.Zero, DIGCF.ALLCLASSES|DIGCF.PROFILE);
SP_DEVINFO_DATA devdata = new SP_DEVINFO_DATA();
devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);
//遍历设备
for (uint i = 0; SetupDiEnumDeviceInfo(info, i, out devdata); i++)
{
    if (devdata.ClassGuid == new Guid("{4d36e96e-e325-11ce-bfc1-08002be10318}"))
    {
        screenNum++;
    }
}
if (info != IntPtr.Zero)
SetupDiDestroyDeviceInfoList(info);

总结

最后这个方法只要是设备管理器中可以看见的设备均可以查询到,拿到更加原始的数据,不过相对的查询速度会慢一些,不过解决了问题,值得庆祝,分享给大家,希望大家少走弯路。

踩坑的道路永不停歇!

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
该动态链接库用于获取windows的所有“设备”信息。支持的设备如下: ////系统硬件设备 Win32_Processor,//CPU处理器 Win32_PhysicalMemory,//物理内存条 Win32_Keyboard,//键盘 Win32_PointingDevice,//点输入设备,包括鼠标。 Win32_FloppyDrive,//软盘驱动器 Win32_DiskDrive,//硬盘驱动器 Win32_CDROMDrive,//光盘驱动器 Win32_BaseBoard,//主板 Win32_BIOS,//BIOS芯片 Win32_ParallelPort,//并口 Win32_SerialPort,//串口 Win32_SerialPortConfiguration,//串口配置 Win32_SoundDevice,//多媒体设置,一般指声卡。 Win32_SystemSlot,//主板插槽(ISA&PCI&AGP) Win32_USBController,//USB控制器 Win32_NetworkAdapter,//网络适配器 Win32_NetworkAdapterConfiguration,//网络适配器设 Win32_Printer,//打印机 Win32_PrinterConfiguration,//打印机设置 Win32_PrintJob,//打印机任务 Win32_TCPIPPrinterPort,//打印机端口 Win32_POTSModem,//MODEM Win32_POTSModemToSerialPort,//MODEM端口 Win32_DesktopMonitor,//显示器 Win32_DisplayConfiguration,//显卡 Win32_DisplayControllerConfiguration,//显卡设置 Win32_VideoController,//显卡细节。 Win32_VideoSettings,//显卡支持的显示模式。 ////操作系统 Win32_TimeZone,//时区 Win32_SystemDriver,//驱动程序 Win32_DiskPartition,//磁盘分区 Win32_LogicalDisk,//逻辑磁盘 Win32_LogicalDiskToPartition,//逻辑磁盘所在分区 Win32_LogicalMemoryConfiguration,//逻辑内存配置 Win32_PageFile,//系统页文件信息 Win32_PageFileSetting,//页文件设置 Win32_BootConfiguration,//系统启动配置 Win32_ComputerSystem,//计算机信息简要 Win32_OperatingSystem,//操作系统信息 Win32_StartupCommand,//系统自动启动程序 Win32_Service,//系统安装的服务 Win32_Group,//系统管理组 Win32_GroupUser,//系统组帐号 Win32_UserAccount,//用户帐号 Win32_Process,//系统进程 Win32_Thread,//系统线程 Win32_Share,//共享 Win32_NetworkClient,//已安装的网络客户端 Win32_NetworkProtocol,//已安装的网络协议 主要方法定义: // 获取Windows的“设备”名称的DataTable public static DataTable GetDevicesTable() // 获取指定设备的信息,忽略大小写 public static List<PropertyDataCollection> GetDeviceInfoCollection(string deviceName) // 获取指定设备的信息,忽略大小写 public static DataTable GetDeviceInfoDataTable(string deviceName)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值