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

C#扩展桌面的处理 解决扩展桌面代码问题 阅读详情
AI摘要

本文详细介绍了如何使用多种方法查询Windows系统中的显示器数量,特别关注在复制模式下失效的问题。通过设备管理器类Guid筛选屏幕设备,最终提供了一种不受模式影响的解决方案。

缘由

最近有个工装的项目,测试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);

总结

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

踩坑的道路永不停歇!

深入Windows内核:C#调用系统API获取系统信息的实战全解 本文介绍了如何使用C#通过Windows API获取系统关键信息,包括系统目录、CPU信息、内存状态、注册表数据和屏幕分辨率等。每个代码示例都包含完整的错误处理和内存安全机制,确保代码的稳定性和可靠性。 基础篇:通过DllImport声明和调用Windows API,获取系统目录和系统时间。使用StringBuilder和ref参数确保内存安全,并通过SetLastError捕获错误。 进阶篇:通过注册表操作获取CPU信息,使用PerformanceCounter实时监控内存使用情况。注册表操作涉及RegO 阅读详情

相关推荐

掌握C#获取系统默认图标的方法

P/Invoke(Platform Invocation Services)是C#中的一种机制,允许程序员从托管代码调用非托管函数,即那些用C或C++等语言编写的本地代码函数。这一特性对于需要调用操作系统底层API、旧有库或硬件设备接口的场景尤为重要。P/Invoke的优势在于,它能够为开发者提供访问底层系统服务的能力,尤其是在.NET框架本身不提供直接支持的场景下。通过P/Invoke,开发者可以不必重新发明轮子,直接利用现有的、经过验证的本地代码库。

weixin_34547628的博客 1220

获取显示屏的个数和分辨率

获取当前显示屏分辨率 就是显示控制台窗口的那个显示屏的分辨率 #include <Windows.h> #include <stdio.h> int main() { int nScreenWidth, nScreenHeight; nScreenWidth = GetSystemMetrics(SM_CXSCREEN)...

weixin_30399821的博客 555

C#得到显示器的分辨率和颜色大小

摘要:C#源码,菜单窗体,分辨率  较简单的C#小程序,获取到当前显示器的分辨率和颜色值大小,其实是从Windows系统内读取设置而得到的,程序本身并没有那么强大的功能,直接去读取显示器内部的属性。

C#桌面程序监听显示器更改事件

项目中使用外接HDMI显示器,显示通知内容。最开始处理的方式是使用Screen.AllScreens获取要显示通知的显示器,然后设置通知窗体的位置在显示器上,这样就能将通知显示到第二个显示器上。而主显示器仍然显示程序主界面内容。但在应用中遇到这样一个问题,当第二显示器关闭的时候,通知窗体,就自动的弹回到主显示器上显示了。然而,当第二显示器,再开的时候,也不会自动将通知窗体显示到第二显示器上。经过努

希望 3480

关于获取多个屏幕分辨率以及进行一些设置1

关于获取多个屏幕分辨率以及进行一些设置1

Verilog实现FIR滤波器

1、FIR滤波器简介 不追究FIR滤波器的深层含义,我们只关注如何实现;可以看出,FIR滤波器的本质就是延迟、系数相乘与求和,如下图: 2、设计 基于以上分析,按照三级流水实现FIR滤波器设计:信号延迟-系数相乘-求和 `timescale 1ns / 1ps module fir( input clk, input rst_n, input [3:0]din, output reg [9:0]dout ); // data reg reg [3:0]din1; reg [3:.

CLL_caicai的博客 1万+

WMI使用的WIN32_类库名

包括:硬件类、操作系统类、安装应用程序类、WMI服务管理类、性能计数器类1、硬件类冷却类别Win32_Fan--风扇Win32_HeatPipe--热管Win32_Refrigeration--致冷Win32_TemperatureProbe--温度传感输入设备类别Win32_Keyboard--键盘 Win32_PointingDevice--指示设备(如鼠标

星星的专栏 1万+

STM32G0实战:HAL库与LL库Flash操作避坑指南(附HardFault解决方案)

本文深入剖析了STM32G0系列微控制器在Flash操作中常见的HardFault问题,对比了HAL库与LL库在读写擦除操作上的底层差异与潜在陷阱。文章提供了基于寄存器状态分析的精准调试方法,并给出了经过实战检验的解决方案与健壮的代码框架,帮助开发者有效规避数据丢失与系统崩溃风险。

t8u9v0w1x的博客 731

获取windows系统的显示器个数

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33691817的博客 1681

Lede(OpenWrt)安装和双宽带叠加

LEDE是一个专为路由器和嵌入式设备设计的自由和开源的操作系统。LEDE是OpenWRT项目的一个分支项目。LEDE操作系统包括许多路由器有用的功能,如网络地址转换、防火墙、端口转发、DHCP服务器等。

我的博客 6441

Excel 汉字首字母提取:四种方案对比与选型

本文对比了 Excel 中提取汉字首字母的四种方案:手动转换、VBA 脚本、映射表与 AI 工具。手动方式低效易错;VBA 适合无网环境但识别范围有限;映射表精度提升却无法解决多音字;AI 工具通过语义理解可一句话完成提取,是多数场景下的首选。

u011186532的专栏 396

C# 获取 Windows 操作系统版本和名称

1. 概述 从 Windows 10 开始,微软已经弃用了 GetVersionEx 方式获取 Windows 系统版本[官方解释]。这就导致网上一大批C#获取 Windows 版本的代码把 Windows 10 识别为 Windows 8。比如我的电脑 Windows 10 就被识别成 6.2.9200,进而判断出是 Windows 8。 按照微软官方论坛提示,可以通过 WMI(Windo...

Abelia的博客 1万+

Challenge 6 - OSCP C

开放了161端口,直接snmpbulkwalk扫描得到账号密码试了那几个web端口,发现可以登录8083的vesta,但是需要用户名大写Jack接着登录之后里面可以创建计划任务,直接弄个反弹shell,一分钟回弹。得到shell后,运行linpeas.sh提供了几个可能的漏洞,挨个试一下。

羽的博客 4142

C#查询显示器得刷新频率

查询显示器得刷新频率。 一、手工操作 windows10系统,开始(右键)—系统—显示—高级显示设置—查看刷新频率和分辨率 二、使用C#查询 public enum DMDO { DEFAULT = 0, D90 = 1, D180 = 2, D270 = 3 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct DEVMODE { public const int DM_DISPLAYFREQUENCY

bianhu3124的博客 1731

华为OD面试手撕真题 - 最大数

华为OD面试手撕真题 最大的数 提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解

qq_45776114的博客 343

DeepSeek与YOLO融合技术的微信小程序实现方案

本文介绍了一个基于微信小程序和Flask后端的智能图像检测系统。系统采用DeepSeek-7B进行自然语言理解,YOLOv8执行目标检测,并通过融合验证模块确保结果准确性。核心流程包括:用户通过小程序上传图片和文本需求,后端解析意图后执行目标检测,最后验证并返回结果。关键技术包括DeepSeek的指令理解、动态阈值调整和模型优化(ONNX、TensorRT)。系统适用于工业质检等场景,如"检测表面划痕",能自动识别目标并验证结果。部署需GPU服务器,并包含安全机制保障系统稳定运行。

老郑的博客 274

C#获取CPU个数

首先添加引用 #region Using directives using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; us

4208

生产环境中redis的SCAN命令如何替代KEYS命令?

本文介绍了使用SCAN命令替代KEYS的完整方案。SCAN通过游标分批迭代,避免KEYS命令遍历整个键空间导致的阻塞风险。文章详细说明了SCAN的基础语法、迭代流程和Java实现示例,强调必须关闭Cursor避免连接泄漏。针对生产环境,提供了COUNT参数调优、客户端去重、集群模式适配等优化建议,并列举了典型错误场景。SCAN通过分批次迭代和游标管理,可安全实现高性能键空间遍历。全文150字。

DN金猿的博客 179

C# 获取系统显示器分辨率大小(多屏显示器)

C# 获取系统显示器屏幕分辨率大小(多显示器)

幻辰科技 nbhctec.com 2272

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

C# Windows获取系统路径汇总

获取操作系统路径汇总(红色为常用) string dir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);//获取系统桌面目录路径。 string dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);//获取“我的文档”文件夹目录路径。 string dir = Environment.GetFolderPath(Envi

qq_44805293的博客 1853

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

C#获取Windows下光标位置

使用C#获取光标相对于显示器屏幕的位置,以下方式在Windows 8的WPF程序中测试通过 方式一: using System; using System.Drawing; using System.Runtime.InteropServices; namespace ColorPicker { /// &lt;summary&gt; /// win8下wpf程序测试成功 ...

岁月无情 5371

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C# 获取显示器物理尺寸

public virtual List<string> GetMonitorPnpDeviceId() { List<string> rt = new List<string>(); using (ManagementClass mc = new ManagementClass("Win32...

weixin_30920853的博客 1413

c# 获取路径的盘符_C#获取当前系统磁盘符、系统目录、桌面等

1.获取方式如下Environment.SpecialFolder中定义了许多常用的目录//获取当前系统磁盘符方法1,返回:C:string path = Environment.GetEnvironmentVariable("systemdrive");//获取当前系统磁盘符方法2,返回:C:string path = Environment.ExpandEnvironmentVariables...

weixin_39609483的博客 3424

C#获取Windows下光标位置(转)

使用C#获取光标相对于显示器屏幕的位置: 方式一: 1 [csharp] view plaincopyprint? 2 using System; 3 using System.Drawing; 4 using System.Runtime.InteropServices; 5 6 namespace ColorPicker 7 { ...

dianloufen8328的博客 471

C#获取Windows系统版本

C#获取Windows系统版本

qq_38652288的博客 1278
上一篇: Docker学习笔记——常用命令(4)
下一篇: C# 禁用启用设备管理器中的设备
码哈
博客等级 码龄8年 6粉丝 22原创
评论 11
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值