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

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. 面试:你知道Java中的回调机制吗?
  2. Failed to load JavaHL Library解决方法
  3. JavaScript学习之对象
  4. 开发和使用Web用户控件
  5. No_16_0303 Java基础学习第十一天
  6. 【每周CV论文推荐】 掌握残差网络必读的10多篇文章
  7. 点击下载!《阿里云SRE技术期刊》2021年5月刊发布啦!
  8. python爬虫 爬取bilibili新番榜
  9. 2021年江苏高考各科成绩查询,江苏2021年高考总分及各科分数
  10. 求你了,别再说Java对象都是在堆内存上分配空间的了!
  11. android反射开启通知_作为Android开发者 你真的知道app从启动到主页显示的过程吗?...
  12. 哔哩哔哩2019年Q4及全年财报:全年营收67.8亿元,同比增长64%
  13. body-parser和multer
  14. 2003下的共享问题
  15. ALFA深度学习软件金属外观缺陷检测应用
  16. FusionSphere虚拟化套件
  17. 上海市医药学校计算机房,上海市医药学校校内比价文件综合楼三楼机房信息发布及广播配套.DOC...
  18. 计算机视觉方向顶级会议和顶级期刊
  19. 统计图配色方案_填充
  20. 批量删除某网站上传的题库

热门文章

  1. HDLBits答案(21)_Verilog有限状态机(8)
  2. MATLAB生成FPGA COE文件之XILINX FPGA滤波器系数
  3. linux有防火墙么,Linux防火墙Firewall和Iptables的使用
  4. JZOJ 2678. 树B
  5. [Java]Thinking in Java 练习2.12
  6. Keepalived+nginx实现高可用负载均衡
  7. luogu P1058 立体图
  8. 单例模式---设计模式
  9. Win7下提取加密PDF文件(pdf加密成exe)
  10. 窗口类、窗口类对象与窗口 三者之间关系