最近做了个三维的程序,部署到客户机器上,客户看了后,现场提出这样的一个需求:程序能智能探测接入的显示器个数,当有新的显示器接入时,现有的只在一个显示器上显示的三维场景能投递到新插入的显示器上显示。类似在桌面上点击鼠标右键,选择“显示设置”菜单,弹出的如下界面:

VC++对话框工程代码实现如下:

.h文件如下:


// displayOperDlg.h: 头文件
//#pragma once
#include<list>
using std::list;// CDisplayOperDlg 对话框
class CDisplayOperDlg : public CDialogEx
{
// 构造
public:CDisplayOperDlg(CWnd* pParent = nullptr);   // 标准构造函数// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_DISPLAYOPER_DIALOG };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持void    GetAllMonitors();void    SwitchPrimaryScreen(int newPrimary, int oldPrimary);void    MoveOldPrimary(int newPrimary, int oldPrimary);void    MoveNewPrimary(int newPrimary, int oldPrimary);void    CommitChange();int    GetPrimaryScreen();int    SetPrimaryScreen(int num);int    SetCloneView(int mode);int    ChangeScreenOrientation(int num, int rotation);
// 实现
protected:HICON m_hIcon;// 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()
public:afx_msg void OnCbnSelchangeCombo1();afx_msg void OnCbnDropdownCombo1();afx_msg void OnBnClickedButton1();afx_msg void OnBnClickedButton2();afx_msg void OnBnClickedOk();private:list<DISPLAY_DEVICE> dev_list;CComboBox* comboBox;list<DEVMODE>dev_mode_list;int PrimaryNum, selIndex, count1;int count{0};
};

.cpp文件如下:


// displayOperDlg.cpp: 实现文件
//#include "pch.h"
#include "framework.h"
#include "displayOper.h"
#include "displayOperDlg.h"
#include "afxdialogex.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx
{
public:CAboutDlg();// 对话框数据
#ifdef AFX_DESIGN_TIMEenum { IDD = IDD_ABOUTBOX };
#endifprotected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现
protected:DECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()// CDisplayOperDlg 对话框CDisplayOperDlg::CDisplayOperDlg(CWnd* pParent /*=nullptr*/): CDialogEx(IDD_DISPLAYOPER_DIALOG, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}void CDisplayOperDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);
}BEGIN_MESSAGE_MAP(CDisplayOperDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDOK, &CDisplayOperDlg::OnBnClickedOk)ON_CBN_SELCHANGE(IDC_COMBO1, &CDisplayOperDlg::OnCbnSelchangeCombo1)ON_CBN_DROPDOWN(IDC_COMBO1, &CDisplayOperDlg::OnCbnDropdownCombo1)ON_CBN_SELCHANGE(IDC_COMBO1, &CDisplayOperDlg::OnCbnSelchangeCombo1)ON_CBN_DROPDOWN(IDC_COMBO1, &CDisplayOperDlg::OnCbnDropdownCombo1)ON_BN_CLICKED(IDC_BUTTON1, &CDisplayOperDlg::OnBnClickedButton1)ON_BN_CLICKED(IDC_BUTTON2, &CDisplayOperDlg::OnBnClickedButton2)ON_BN_CLICKED(IDOK, &CDisplayOperDlg::OnBnClickedOk)
END_MESSAGE_MAP()// CDisplayOperDlg 消息处理程序BOOL CDisplayOperDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != nullptr){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);            // 设置大图标SetIcon(m_hIcon, FALSE);        // 设置小图标GetAllMonitors();CComboBox* comboBox = (CComboBox*)GetDlgItem(IDC_COMBO1);comboBox->ResetContent();for (int i = 0; i < dev_list.size(); i++){CString string1;//; = CString(i);string1.Format(_T("%d"), i + 1);//ZeroMemory(&string1, sizeof(string1));//sprintf(temp, "%d", i+1);comboBox->AddString(string1);}comboBox->SetCurSel(0);UpdateData(false);return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}void CDisplayOperDlg::GetAllMonitors()
{std::list<DISPLAY_DEVICE> devices;std::list<DEVMODE> modes; int devId = 0;bool ret = false;bool isPrimary = false;//list all DisplayDevices (Monitors)do{DISPLAY_DEVICE displayDevice;ZeroMemory(&displayDevice, sizeof(DISPLAY_DEVICE));displayDevice.cb = sizeof(displayDevice);ret = EnumDisplayDevices(NULL, devId, &displayDevice, 0);if (ret == true){if ((displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == DISPLAY_DEVICE_ATTACHED_TO_DESKTOP){devices.push_back(displayDevice);isPrimary = ((displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) == DISPLAY_DEVICE_PRIMARY_DEVICE);if (isPrimary)PrimaryNum = devId;}}devId++;} while (ret);dev_list = devices;std::list<DISPLAY_DEVICE>::iterator it;for (it = dev_list.begin(); it != dev_list.end(); it++){DEVMODE deviceMode;deviceMode.dmSize = sizeof(DEVMODE);deviceMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS; // | DM_DISPLAYORIENTATION;EnumDisplaySettings(it->DeviceName, (int)ENUM_REGISTRY_SETTINGS, &deviceMode);modes.push_back(deviceMode);}dev_mode_list = modes;}
void CDisplayOperDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。void CDisplayOperDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作区矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();}
}//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CDisplayOperDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}void CDisplayOperDlg::SwitchPrimaryScreen(int newPrimary, int oldPrimary)
{MoveNewPrimary(newPrimary, oldPrimary);MoveOldPrimary(newPrimary, oldPrimary);CommitChange();
}void CDisplayOperDlg::MoveOldPrimary(int newPrimary, int oldPrimary)
{int index = 0;std::list<DISPLAY_DEVICE>::iterator it1;for (it1 = dev_list.begin(); it1 != dev_list.end(); it1++){if (index == oldPrimary)break;index++;}index = 0;std::list<DEVMODE>::iterator it2;for (it2 = dev_mode_list.begin(); it2 != dev_mode_list.end(); it2++){if (index == newPrimary)break;index++;}index = 0;std::list<DEVMODE>::iterator it3;for (it3 = dev_mode_list.begin(); it3 != dev_mode_list.end(); it3++){if (index == oldPrimary)break;index++;}it3->dmPosition.x = it2->dmPelsWidth;it3->dmPosition.y = 0;DEVMODE deviceMode = *it3;int ret = ChangeDisplaySettingsEx(it1->DeviceName, &deviceMode, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL);
}void CDisplayOperDlg::MoveNewPrimary(int newPrimary, int oldPrimary)
{int index = 0;std::list<DISPLAY_DEVICE>::iterator it1;for (it1 = dev_list.begin(); it1 != dev_list.end(); it1++){if (index == newPrimary)break;index++;}index = 0;std::list<DEVMODE>::iterator it2;for (it2 = dev_mode_list.begin(); it2 != dev_mode_list.end(); it2++){if (index == newPrimary)break;index++;}it2->dmPosition.x = 0;it2->dmPosition.y = 0;DEVMODE deviceMode = *it2;int ret = ChangeDisplaySettingsEx(it1->DeviceName, &deviceMode, NULL, CDS_SET_PRIMARY | CDS_UPDATEREGISTRY | CDS_NORESET, NULL);
}void CDisplayOperDlg::CommitChange()
{ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
}int CDisplayOperDlg::GetPrimaryScreen()
{return PrimaryNum;
}int CDisplayOperDlg::SetPrimaryScreen(int num)
{int oldprimary = GetPrimaryScreen();int newPrimary = num;if ((num >= dev_list.size()) || (num < 0))return -1;if (oldprimary == newPrimary)return 0;SwitchPrimaryScreen(newPrimary, oldprimary);PrimaryNum = newPrimary;return oldprimary;
}int CDisplayOperDlg::SetCloneView(int mode)
{/*UINT32 PathArraySize = 0;UINT32 ModeArraySize = 0;DISPLAYCONFIG_PATH_INFO* PathArray;DISPLAYCONFIG_MODE_INFO* ModeArray;DISPLAYCONFIG_TOPOLOGY_ID CurrentTopology;//Determine the size of the path array that is required to hold all valid pathsGetDisplayConfigBufferSizes(QDC_ALL_PATHS, &PathArraySize, &ModeArraySize); //retrieve the sizes of the DISPLAYCONFIG_PATH_INFO and DISPLAYCONFIG_MODE_INFO buffers that are required//Allocate memory for path and mode information arraysPathArray = (DISPLAYCONFIG_PATH_INFO*)malloc(PathArraySize * sizeof(DISPLAYCONFIG_PATH_INFO));memset(PathArray, 0, PathArraySize * sizeof(DISPLAYCONFIG_PATH_INFO));ModeArray = (DISPLAYCONFIG_MODE_INFO*)malloc(ModeArraySize * sizeof(DISPLAYCONFIG_MODE_INFO));ZeroMemory(ModeArray, ModeArraySize * sizeof(DISPLAYCONFIG_MODE_INFO));//Request all of the path informationLONG ret = QueryDisplayConfig(QDC_DATABASE_CURRENT,&PathArraySize, PathArray, &ModeArraySize, ModeArray, &CurrentTopology); //obtain the path and mode information for all posible paths// Above CurrentTopology variable will aquire the current display setting (ie Extend, Duplicate etc)free(PathArray);free(ModeArray);*///Set the new topology.SetDisplayConfig(0, NULL, 0, NULL, mode | SDC_APPLY); //change to the clone topologyreturn 0;
}int CDisplayOperDlg::ChangeScreenOrientation(int num, int rotation)
{int index = 0;std::list<DEVMODE>::iterator it;for (it = dev_mode_list.begin(); it != dev_mode_list.end(); it++){if (index == num)break;index++;}DWORD dwTemp = it->dmPelsHeight;switch (rotation){case 0:if (it->dmDisplayOrientation == DMDO_DEFAULT)it->dmDisplayOrientation = DMDO_90;else if (it->dmDisplayOrientation == DMDO_90)it->dmDisplayOrientation = DMDO_DEFAULT;it->dmPelsHeight = it->dmPelsWidth;it->dmPelsWidth = dwTemp;break;case 1:if (it->dmDisplayOrientation == DMDO_DEFAULT)it->dmDisplayOrientation = DMDO_90;else if (it->dmDisplayOrientation == DMDO_90)it->dmDisplayOrientation = DMDO_DEFAULT;it->dmPelsHeight = it->dmPelsWidth;it->dmPelsWidth = dwTemp;break;}DEVMODE deviceMode;ZeroMemory(&deviceMode, sizeof(deviceMode));deviceMode.dmSize = sizeof(deviceMode);deviceMode = *it;index = 0;std::list<DISPLAY_DEVICE>::iterator it1;for (it1 = dev_list.begin(); it1 != dev_list.end(); it1++){if (index == num)break;index++;}//long lRet = ChangeDisplaySettingsEx(it1->DeviceName, &deviceMode, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL);//CommitChange();long lRet = ChangeDisplaySettingsEx(it1->DeviceName, &deviceMode, NULL, CDS_UPDATEREGISTRY, NULL);return lRet;
}void CDisplayOperDlg::OnCbnSelchangeCombo1()
{//取得选中的值CString selStr;int selIndex = comboBox->GetCurSel();//取得选中的索引comboBox->GetLBText(selIndex,selStr);MessageBox(selStr);
}void CDisplayOperDlg::OnCbnDropdownCombo1()
{GetAllMonitors();comboBox = (CComboBox*)GetDlgItem(IDC_COMBO1);comboBox->ResetContent();for (int i = 0; i < dev_list.size(); i++){CString string1;//; = CString(i);string1.Format(_T("%d"), i + 1);//ZeroMemory(&string1, sizeof(string1));//sprintf(temp, "%d", i+1);comboBox->AddString(string1);}UpdateData(false);
}void CDisplayOperDlg::OnBnClickedButton1()
{long lRet;selIndex = comboBox->GetCurSel();//取得选中的索引if (selIndex < 0)return;count1++;if (count1 % 2){lRet = ChangeScreenOrientation(selIndex, 1);}else{lRet = ChangeScreenOrientation(selIndex, 0);}
}void CDisplayOperDlg::OnBnClickedButton2()
{count++;if (count % 2){SetCloneView(SDC_TOPOLOGY_CLONE);GetDlgItem(IDC_BUTTON2)->SetWindowTextW(_T("恢复"));}else{SetCloneView(SDC_TOPOLOGY_EXTEND);GetDlgItem(IDC_BUTTON2)->SetWindowTextW(_T("屏幕复制"));}
}void CDisplayOperDlg::OnBnClickedOk()
{SetPrimaryScreen(selIndex);//
//  CDialogEx::OnOK();
}

参考链接:

《VC++实现Windows中双显示器(主屏、扩展屏)各种操作的源码工程》

附上几个有用的链接:

https://msdn.microsoft.com/en-us/library/ff569533(v=vs.85).aspx

https://msdn.microsoft.com/en-us/library/dd183413(VS.85).aspx

VC++编程实现多显示器控制(复制、横屏、纵屏,显示器个数)相关推荐

  1. C++编程实现多显示器控制(复制、横屏、纵屏,显示器个数)等

    需求的提出: 最近做了个三维的程序,部署到客户机器上,客户看了后,现场提出这样的一个需求:程序能智能探测接入的显示器个数,当有新的显示器接入时,现有的只在一个显示器上显示的三维场景能投递到新插入的显示 ...

  2. 入党对程序员有什么用_为什么程序员都喜欢用两个大屏显示器?网友:一个复制一个粘贴...

    如今的程序员似乎对办公电脑的配置要求非常高,连一些相对普通的配置都不入他们的"法眼"!都要比较先进的配置.不过想想也是,毕竟电脑是他们天天打交道的玩意.然而笔者却发现,程序员的办公 ...

  3. VC++编程中的文件操作API和CFile类

    VC++编程中的文件操作API和CFile类 在VC编程中,操作文件的方法主要有两种:利用API函数和MFC的CFile类.微软在其中封装了文件的一般操作,下面我就介绍一下如何利用这两种方法实现文件操 ...

  4. 《实用VC编程之玩转控件》第1课:Windows编程简介

    本文转载自: VC驿站 https://www.cctry.com/thread-297374-1-1.html 0.开发环境: 操作系统:Microsoft Windows 7 Ultimate S ...

  5. 自学python单片机编程-用Python语言控制单片机

    早年,虽然Python是一款比较容易上手的脚本语言,而且有强大的社区支持,一些非计算机专业领域的人都选它作为入门语言.遗憾的是,它不能实现一些非常底层的操控,所以在硬件领域并不起眼.本文引用地址:ht ...

  6. 自学python单片机编程-用Python语言控制单片机-可编程逻辑-与非网

    早年,虽然 Python 是一款比较容易上手的脚本语言,而且有强大的社区支持,一些非计算机专业领域的人都选它作为入门语言.遗憾的是,它不能实现一些非常底层的操控,所以在硬件领域并不起眼. 然而今时不同 ...

  7. Python黑帽编程2.4 流程控制

    Python黑帽编程2.4  流程控制 本节要介绍的是Python编程中和流程控制有关的关键字和相关内容. 2.4.1 if -..else 先上一段代码: #!/usr/bin/python # - ...

  8. 用python语言实现人工智能猴子摘香蕉的问题_人工智能实验报告大全:猴子摘香蕉问题的VC编程实现等八次.docx...

    人工智能课内实验报告(8次)学 院: 自动化学院 班 级: 智能1501 姓 名: 刘少鹏(34) 学 号: 目 录课内实验1:猴子摘香蕉问题的VC编程实现--------1课内实验2:编程实现简单动 ...

  9. VC编程实现IE风格的界面

    使用过IE浏览器的朋友都知道IE界面上的扁平工具条.地址栏,扁平工具栏上的按钮正常状态下为扁平态,按钮上的图像为灰色,当鼠标放在按钮上时,按钮突 起(这种状态称为手柄),并且其上的图像变得鲜艳醒目,一 ...

  10. java 线程 condition_Java编程中实现Condition控制线程通信

    java中控制线程通信的方法 1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyall()控制线程通信.不灵活. 2.利用condition ...

最新文章

  1. uniapp中qrcode生成二维码后传的参数不见了_阿虚教你制作动态二维码,超详细教程!
  2. MOD函数语法和参数
  3. 3dsmax biped 骨骼的创建和修改
  4. 由一个园友因为上传漏洞导致网站被攻破而得到的教训
  5. 大流量高并发量网站的之解决方案
  6. python3 字典 dict 创建 添加 修改 复制 取值 删除
  7. python模块 - re模块使用演示样例
  8. 学python是看书还是看视频-学 Python 你觉得是看书还是看视频?
  9. 仿联想商城laravel实战---7、lavarel中如何给用户发送邮件
  10. github使用ssh key
  11. 在ASP.NET中怎么用SESSION判断用户是否登录
  12. matlab3dimshow_matlab利用MATLAB嵌套函数表示3D作图
  13. 算法练习day8——190326(猫狗队列、转圈打印矩阵、旋转正方形矩阵、反转单向双向链表、数N的加法组合)
  14. QT的QRenderPass类的使用
  15. Ubuntu 创建文件夹时出现:用户名 不在 sudoers 文件中。此事将被报告。
  16. 还服务器网站被k,导致网站被K的主要原因,看看你有没有中招!
  17. mysql bcp数据文件_BCP 数据的导入和导出
  18. Android studio 设置签名
  19. 今天要学习的技术点,Python 筛选数字,模块导入,特殊变量__all__ 实战博客
  20. c#自定义日历插件,给重要日期添加色彩。以及系统自带的monthCalendar日历插件

热门文章

  1. 真人出镜,微信视频号第一期视频来了!
  2. java intfilter_Java IntStream filter()用法及代码示例
  3. JAVA子类是球父类是圆_java的父类和子类,这是矛盾...
  4. 计算机专业高校鄙视链,清华大学“鄙视链”,本科生看不起研究生?本科非清华不算清华人...
  5. oracle归档日志保留时间,Oracle 11g Data Guard 备库归档日志清理脚本(保留一周归档)...
  6. java学习_Java编程学习难不难 怎样才能快速入门Java
  7. c iostream.源码_通达信《牛气冲天》指标,共振主升浪冲涨停,牛散经常用(附源码...
  8. CTF刷题网站汇总(包括本地可以自己搭建的)(1)
  9. 使用HTML5技术控制电脑或手机上的摄像头
  10. u-boot分析(四)---设置异常向量表|设置SVC模式