C++检测多显示器并把窗口显示在不同显示器上(完整源码)

news/2024/4/28 7:12:45/文章来源:https://blog.csdn.net/2301_77171572/article/details/136927587

初级代码游戏的专栏介绍与文章目录-CSDN博客

        早先大部分应用都不考虑多显示的问题。

        如果是多窗口应用,子窗口不会被限制在父窗口里面的,可以轻松把窗口拖到不同的显示器上。

        但是很多流行的界面都是一个全屏主窗口,然后其他窗口都只能在主窗口范围内,这种程序就没法自动适应多显示器了。

        但是现在专门针对多显示器的需求增多了,比如视频监控类的应用,用户会很喜欢使用多显示器,像这样就需要程序支持多显示器,并且最好是自动使用多显示器而不需要用户拖放窗口。

目录

一、多显示器概要

二、获取显示器数量和坐标

三、显示窗口到副显示器

四、相关技术点

4.1 EnumDisplayDevices

4.2  EnumDisplaySettingsEx

4.3 GetSystemMetrics(SM_CMONITORS)

4.4 MFC的MoveWindow


一、多显示器概要

        多显示器共享同一个屏幕坐标空间,主显示器左上角为(0,0),其余显示器排列在其它位置,坐标可能是负值。具体如何取决于显示设置,可以用鼠标拖放显示器来控制显示器的位置关系。

        多个显示器的坐标可能会有间隔!原因是窗口最大化时阴影和边框位于显示器外面,但是又不能出现在别的显示器上,所以显示器之间有间隔。这些问题可以通过获取窗口坐标来验证。

二、获取显示器数量和坐标

        获取所有显示器信息的代码:

RECT m_ScrRect[10];
int GetScreenRect()
{int count = 0;for (int ScreenNo = 0;true; ++ScreenNo){BOOL flag;DISPLAY_DEVICE dd;ZeroMemory(&dd, sizeof(dd));dd.cb = sizeof(dd);//枚举显示器,获取后面要用的名字,注意这会返回系统所能支持的所有显示器,ScreenNo从0开始,直到返回FALSEflag = EnumDisplayDevices(NULL, ScreenNo, &dd, EDD_GET_DEVICE_INTERFACE_NAME);if (!flag){break;}DEVMODE dm;ZeroMemory(&dm, sizeof(dm));dm.dmSize = sizeof(dm);//返回当前设置,如果失败表明显示器不在线flag = EnumDisplaySettingsEx(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm, 0);if (!flag){continue;}m_ScrRect[count].left = dm.dmPosition.x;//如果副显示器在左边,则这个值是负的m_ScrRect[count].top = dm.dmPosition.y;m_ScrRect[count].right = m_ScrRect[count].left + dm.dmPelsWidth - 1;m_ScrRect[count].bottom = m_ScrRect[count].top + dm.dmPelsHeight - 1;++count;thelog << dd.DeviceName << " (dmBitsPerPel "<<dm.dmBitsPerPel <<" dmLogPixels" <<dm.dmLogPixels << ") " << dm.dmPosition.x << " " << dm.dmPosition.y << " " << dm.dmPelsWidth << " " << dm.dmPelsHeight << endi;}thelog << "检索到显示器 " << count << endi;return count;
}

        注意这段代码假设显示器最多有10个。thelog是日志,endi表示一般信息,可以用cout和endl代替。后面的测试代码会用这个函数获取多显示器信息。

        相关技术点:

  • EnumDisplayDevices 枚举系统所有的显示器,这是系统所支持的最大数量,每个都预先分配了名字
  • EnumDisplayDevices 获取显示器信息,如果显示器不存在就会返回失败,但是存在的显示器并不是连续的,所以每个都要检查

三、显示窗口到副显示器

        修改显示about对话框的代码(对话框项目),显示在副显示器上:

//这里改成了非模态对话框,用来验证多显示器支持,里面记录的奇怪的问题都是因为项目设置没有设置“每个监视器高DPI识别”导致的
CAboutDlg dlgAbout;
void CTestDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){//奇怪的问题,在副屏上如果已经是最大化,再次打开就会消失,但是主屏不会//计算得到的RECT和实测的不一样,实测的x似乎大一倍//由于尺寸问题,最大化可以在副屏显示,但是要先隐藏,不然再次打开就消失,而且再也不会出现if (!dlgAbout.GetSafeHwnd()){dlgAbout.Create(IDD_ABOUTBOX, this);}else { thelog << "无模式对话框已存在" << ende; }int monitors = GetSystemMetrics(SM_CMONITORS);//这个直接返回显示器个数if (GetScreenRect() != monitors){thelog << "检索显示器信息出错" << ende;}else{RECT* pMonitorRect = &m_ScrRect[monitors - 1];//取最后一个显示器RECT rect;dlgAbout.GetWindowRect(&rect);rect.right = pMonitorRect->left + rect.right - rect.left;rect.bottom = pMonitorRect->top + rect.bottom - rect.top;rect.left = pMonitorRect->left;rect.top = pMonitorRect->top;thelog << "目标 " << rect.left << " " << rect.top << " " << rect.right << " " << rect.bottom << endi;dlgAbout.MoveWindow(&rect);//很奇怪,这样就不显示,最大化就显示SW_SHOWMAXIMIZEDif (0 == dlgAbout.ShowWindow(SW_SHOW))thelog << "ShowWindow成功" << endi;else thelog << "ShowWindow已经是显示的" << ende;RECT curr;dlgAbout.GetWindowRect(&curr);thelog << "实际" << curr.left << " " << curr.top << " " << curr.right << " " << curr.bottom << endi;}if (0==dlgAbout.ShowWindow(SW_SHOW))thelog << "ShowWindow成功" << endi;else thelog << "ShowWindow已经是显示的" << ende;}else{CDialogEx::OnSysCommand(nID, lParam);}
}

        这个代码自动把窗口显示在副显示器上,并测试了放大缩小等窗口操作。

        自动显示到副显示器很简单,就是把窗口移动到副显示器的坐标范围内而已。 

        以上代码是win10+VS2017环境的。

四、相关技术点

4.1 EnumDisplayDevices

winuser.h
User32.dll/User32.libBOOL EnumDisplayDevicesA([in]  LPCSTR           lpDevice,[in]  DWORD            iDevNum,[out] PDISPLAY_DEVICEA lpDisplayDevice,[in]  DWORD            dwFlags
);

        这个函数列举所有设备,知道设备索引超出最大值。所以调用要这个函数需要一个循环,并不断递增iDevNum,直到调用失败。但每个成功返回的设备并不一定是在线的,看起来就是系统预先准备了所支持的最大数量的入口。

4.2  EnumDisplaySettingsEx

Winuser.h
User32.dll/User32.libBOOL EnumDisplaySettingsExA([in]  LPCSTR   lpszDeviceName,[in]  DWORD    iModeNum,[out] DEVMODEA *lpDevMode,[in]  DWORD    dwFlags
);

        这个函数获取具体一个显示器的信息,设备名来自之前的枚举过程,显示器不在线就会失败。系统分配显示器索引并不是连续的,所以要逐个检查之前用EnumDisplayDevices获得的有效设备名。

4.3 GetSystemMetrics(SM_CMONITORS)

Winuser.h
User32.dll/User32.libint GetSystemMetrics([in] int nIndex
);

        这个调用直接获取显示器个数,但是不能获取显示器信息。

        参数nIndex指示不同的功能,比如SM_CMOUSEBUTTONS表示鼠标按钮数量。

4.4 MFC的MoveWindow

        这个函数用来改变窗口位置。

(这里是文档结束)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1026834.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker 的网络管理

docker应用自带了三种类型的网络&#xff0c;然后我们自己也能自定义网络 roottest-virtual-machine:~# docker network ls NETWORK ID NAME DRIVER SCOPE 4c3e28760cff bridge bridge local afd1493dc119 host host local 5f200e2eaf22 n…

AOP切入点表达式基本格式

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 官方地址 https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html AOP切入点表达式基本格式如下&#xff1a; execution(modifiers-patte…

Vscode创建php项目

1.安装中文插件&#xff08;可安装可不安装&#xff09; 2.安装主题&#xff08;可安装可不安装&#xff09; 3.安装和php相关的插件 4.打开文件夹 5.路由操作 查看项目中的route路由 浏览器中访问think 隐藏index.php入口文件 访问ThinkPHP5.1开发手册&#xff0c;复制apa…

React-1-jsx基础-事件绑定-样式处理

一.JSX基础-概念和本质 1.1 什么是JSX JSX是JavaScript和XML&#xff08;HTML&#xff09;的缩写&#xff0c;表示在JS代码中编写HTML模版结构,它是React中编写UI模版的方式 优势&#xff1a; 1. HTML的声明式模版写法 2. JS的可编程能力 JSX的本质&#xff1a; JSX并不是标…

[openGL] qt5版本+mingw编译Assimp库+调用

目录 一 版本 二 编译问题 三 CMAKE准备 四 开始编译 4.1 准备Assimp源码 4.2 编译工具准备 4.3 生成Assimp库 4.4 使用Assimp 4.4.1 准备 4.4.2 加载模型 4.4.3 模型效果 一 版本 Assimp官网上已经停止更新截至在3.3.1版本,但是这个版本编译是最稳定的,较新的版本…

WORDPRESS从WORD复制粘贴公式

整合教程&#xff1a;WordPress插件包整合教程 WordPaster支持自动上传本地图片文件&#xff0c;自动上传Word文档中的图片 步骤与效果&#xff1a; 1.打开word文档&#xff0c;复制word文档内容 2.在网页中打开编辑器页面&#xff0c;点击“粘贴本地文件,Word文档”按钮上传…

GBase8a-GDCA认证考试-复习参考题

个人能力有限&#xff0c;正确率97%&#xff08;97分&#xff09;。 请注意甄别&#xff0c;根据所学知识综合判断&#xff0c;欢迎指出错误答案。 欢迎学习天津南大通用数据技术股份有限公司|GBASE-致力于成为用户最信赖的数据库产品供应商 免费参加认证培训&#xff1a;为…

Visio中存在问题的解决方法

公式缩放 mathtype公式在visio缩放之后&#xff0c;出现了变形。 解决方法&#xff1a;每次输入公式都通过 插入->对象->mathType Equation 新建一个公式。可以避免 注&#xff1a;网上有的说在word中使用mathtype编写公式&#xff0c;之后复制到visio中。 插入波形 选择…

Java的IDEA的工程管理

模块和包的图标&#xff1a; 举个例子&#xff1a; IDEA中创建包&#xff1a; 如图所示&#xff0c;com.LBJ的意思是在com包中创建子包LBJ 参见&#xff1a; IDEA中项目、模块和包的关系_idea中模块和项目-CSDN博客

应用层协议之DNS协议

一.应用层协议的相关数据传输格式 1.文本字符串格式 应用层主要是自定义协议&#xff0c;以点外卖为例&#xff1a; 客户点开软件&#xff0c;就是应用程序和服务器之间进行网络通信交互。请求和响应可以如下设置 请求&#xff1a;用户信息&#xff0c;位置信息&#xff0c…

Vue模块化开发步骤—遇到的问题—解决办法

目录 1.npm install webpack -g 2.npm install -g vue/cli-init 3.初始化vue项目 4.启动vue项目 Vscode初建Vue时几个需要注意的问题-CSDN博客 1.npm install webpack -g 全局安装webpack 直接命令提示符运行改指令会报错&#xff0c;operation not permitted 注意&#…

【QT入门】 Qt代码创建布局之水平布局、竖直布局详解

往期回顾&#xff1a; 【QT入门】 Qt实现自定义信号-CSDN博客 【QT入门】 Qt自定义信号后跨线程发送信号-CSDN博客 【QT入门】 Qt内存管理机制详解-CSDN博客 【QT入门】 Qt代码创建布局之水平布局、竖直布局详解 先看两个问题&#xff1a; 1、ui设计器设计界面很方便&#xf…

1学习使用axios

一、axios介绍&#xff1a; axios 是一个基于 Promise 的 HTTP 客户端&#xff0c;用于浏览器和 Node.js。它提供了一种简单的方法来发送 HTTP 请求&#xff0c;并且具有很多实用的功能&#xff0c;使得网络请求变得更加方便和可靠。 以下是 axios 的一些主要特点和功能&…

python判断当前日期是全年哪一天

设计者&#xff1a;ISDF 版本&#xff1a;v3..0 日期&#xff1a;04/01/2019设计者&#xff1a;ISDF 版本&#xff1a;v4..0 日期&#xff1a;03/27/2024 import datetime#闰年判断函数 def ys_leep_year(year):ys_leep Falseif (year % 400 0) or ((year % 4 0) and (year …

【每日力扣】452. 用最少数量的箭引爆气球与763. 划分字母区间

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害。 452. 用最少数量的箭引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0…

SpringBoot学习之ElasticSearch下载安装和启动(Windows版)(三十)

本文先写windows下的下载安装和启动,后续有时间再补充其他环境下(Mac、Linux、Docker)的,这里我们后续对ElasticSearch简称为ES,读者习惯这一称呼就好。 一,ES下载 可以百度【ElasticSearch官网】或者直接点击这里的ES官网下载地址:​​​​​ Download Elasticsearch…

【 MyBatis 】| 关于多表联查返回 List 集合只查到一条的 BUG

目录 一. &#x1f981; 写在前面二. &#x1f981; 探索过程2.1 开端 —— 开始写 bug2.2 发展 —— bug 完成2.3 高潮 —— bug探究2.4 结局 —— 效果展示 三. &#x1f981; 写在最后 一. &#x1f981; 写在前面 今天又是 BUG 气满满的一天&#xff0c;一个 xxxMapper.xm…

聊聊低代码产品的应用场景

随着数字化转型的不断深入&#xff0c;企业对于快速开发和迭代软件应用的需求也越来越迫切。而在这样的背景下&#xff0c;低代码产品应运而生&#xff0c;成为了一种热门的技术解决方案。本文将解读低代码产品的定义并探讨其应用场景。 一、低代码产品的定义 低代码产品是一种…

恢复 Linux 上已删除的文件:extundelete 、PhotoRec (***)

为什么Linux的命令 rm 没有回收站呢&#xff1f;Trash-Cli&#xff1a;Linux 命令行回收站工具 &#xff08;***&#xff09; https://blog.csdn.net/ken2232/article/details/136981360 extundelete 直接 apt 安装&#xff0c;运行出现段错误&#xff0c;网络上给出的一种解决…

vscode添加gitee

1.创建仓库 2.Git 全局设置 3.初始化仓库 2.1 打开vscode打开需要上传到给git的代码文件 2.2.点击左边菜单第三个的源代码管理->初始化仓库 4.点击加号暂存所有更改 5.添加远程仓库 5.1 添加地址&#xff0c;回车 5.2 填写库名&#xff0c;回车 6.提交和推送 6.1 点击✔提交…