C# WinForm如何全场景检测HDMI/VGA等显示器线缆连接状态?
我来帮你搞定这个全场景显示器状态检测的问题!其实核心就是把应用启动时的初始状态扫描和运行时的动态插拔监听结合起来——你已经搞定了后半部分,现在补上开头的扫描就齐活了。下面给你一步步拆解,附代码示例:
一、启动时扫描初始连接状态
Windows系统本身没有给WinForm直接提供获取所有显示器状态的API,咱们得调用原生的Windows API来枚举设备。主要用到EnumDisplayDevices这个函数,它能列出所有已连接的显示设备,包括启动前就插好的。
首先,先定义需要的P/Invoke结构和函数(直接复制到你的WinForm项目里就行):
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; public partial class MainForm : Form { // 定义DISPLAY_DEVICE结构体,存储显示器设备信息 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] private struct DISPLAY_DEVICE { public int cb; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string DeviceName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceString; public int StateFlags; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceID; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceKey; } // 导入EnumDisplayDevices函数 [DllImport("user32.dll")] private static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags); // 状态标志常量:显示器已连接到桌面(即处于活跃/已启用状态) private const int DISPLAY_DEVICE_ATTACHED_TO_DESKTOP = 0x00000001; // 状态标志常量:显示器是活动的(可能未启用但已连接) private const int DISPLAY_DEVICE_ACTIVE = 0x00000004;
然后写一个获取当前已连接显示器的方法:
// 获取所有已连接的显示器信息 private List<string> GetConnectedDisplays() { List<string> connectedDisplays = new List<string>(); DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE(); displayDevice.cb = Marshal.SizeOf(displayDevice); uint deviceIndex = 0; // 循环枚举所有显示设备 while (EnumDisplayDevices(null, deviceIndex, ref displayDevice, 0)) { // 判断设备是否已连接且活跃 if ((displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != 0 || (displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE) != 0) { connectedDisplays.Add($"{displayDevice.DeviceString} (设备名: {displayDevice.DeviceName})"); } deviceIndex++; displayDevice.cb = Marshal.SizeOf(displayDevice); } return connectedDisplays; }
接下来,在Form的Load事件里调用这个方法,就能拿到启动前已连接的显示器了:
private void MainForm_Load(object sender, EventArgs e) { // 启动时扫描初始状态 var initialDisplays = GetConnectedDisplays(); if (initialDisplays.Count > 0) { string message = "应用启动时已连接的显示器:\n" + string.Join("\n", initialDisplays); MessageBox.Show(message, "初始显示器状态", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("应用启动时未检测到已连接的显示器", "初始显示器状态", MessageBoxButtons.OK, MessageBoxIcon.Information); } // 初始化最后一次状态,用于后续对比 _lastDisplays = new List<string>(initialDisplays); }
二、保留并完善运行时的动态插拔监听
你之前已经实现了运行时的检测,这里可以把监听逻辑和上面的获取方法结合,每次收到显示器变化消息时重新扫描,对比状态差异:
// 存储上一次的显示器状态,用于对比变化 private List<string> _lastDisplays = new List<string>(); protected override void WndProc(ref Message m) { // 监听WM_DISPLAYCHANGE消息:显示器分辨率/连接状态变化时触发 const int WM_DISPLAYCHANGE = 0x007E; if (m.Msg == WM_DISPLAYCHANGE) { var currentDisplays = GetConnectedDisplays(); // 找出新增的显示器 var addedDisplays = currentDisplays.Except(_lastDisplays).ToList(); // 找出移除的显示器 var removedDisplays = _lastDisplays.Except(currentDisplays).ToList(); if (addedDisplays.Count > 0) { string message = "检测到新增显示器:\n" + string.Join("\n", addedDisplays); MessageBox.Show(message, "显示器连接", MessageBoxButtons.OK, MessageBoxIcon.Information); } if (removedDisplays.Count > 0) { string message = "检测到显示器断开:\n" + string.Join("\n", removedDisplays); MessageBox.Show(message, "显示器断开", MessageBoxButtons.OK, MessageBoxIcon.Warning); } // 更新最后一次的状态 _lastDisplays = new List<string>(currentDisplays); } base.WndProc(ref m); } }
一些注意事项
- 状态标志的选择:
DISPLAY_DEVICE_ATTACHED_TO_DESKTOP表示显示器是当前桌面的一部分(已启用),而DISPLAY_DEVICE_ACTIVE表示设备已连接但可能未启用(比如扩展屏但没打开),根据你的需求可以调整判断逻辑。 - 设备信息的扩展:如果需要更详细的信息(比如HDMI/VGA接口类型),可以进一步调用
EnumDisplaySettingsEx函数获取显示器的详细参数,或者通过DeviceID解析硬件信息。 - 兼容性:这个方法在Windows 7及以上系统都能正常工作,WinForm项目直接使用即可。
这样一来,你的应用既能在启动时扫描所有已连接的显示器,又能实时监听插拔变化,实现全场景的检测啦!
内容的提问来源于stack exchange,提问作者saeed




