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

177 阅读3分钟

本文已参与 [新人创作礼] 活动,一起开启掘金创作之路。

Windows中鼠标右键桌面->“屏幕分辨率”时出现的“更改显示器的外观”对话框下实现了双屏操作的诸多功能,如:主屏的设置、主屏和扩展屏的分辨率、方向、屏幕合并等。实际项目中需要通过VC++代码实现这些功能,用了将近一周的事件,在网上经过几番搜索、整合及改写,终于开发出了所需功能。以下将cpp源码贴出,以弥补此方面网上资料的匮乏。

Multi_DisplayDlg.cpp : 实现文件

#include "stdafx.h"#include "Multi_Display.h"#include "Multi_DisplayDlg.h"#include "afxdialogex.h"#ifdef _DEBUG#define new DEBUG_NEW#endif// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx{public: CAboutDlg();// 对话框数据 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现protected: DECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD){}void CAboutDlg::DoDataExchange(CDataExchange* pDX){ CDialogEx::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)END_MESSAGE_MAP()// CMulti_DisplayDlg 对话框CMulti_DisplayDlg::CMulti_DisplayDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CMulti_DisplayDlg::IDD, pParent){ m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);}void CMulti_DisplayDlg::DoDataExchange(CDataExchange* pDX){ CDialogEx::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CMulti_DisplayDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDOK, &CMulti_DisplayDlg::OnBnClickedOk) ON_CBN_SELCHANGE(IDC_COMBO1, &CMulti_DisplayDlg::OnCbnSelchangeCombo1) ON_CBN_DROPDOWN(IDC_COMBO1, &CMulti_DisplayDlg::OnCbnDropdownCombo1) ON_BN_CLICKED(IDC_BUTTON1, &CMulti_DisplayDlg::OnBnClickedButton1) ON_BN_CLICKED(IDC_BUTTON2, &CMulti_DisplayDlg::OnBnClickedButton2)END_MESSAGE_MAP()// CMulti_DisplayDlg 消息处理程序BOOL CMulti_DisplayDlg::OnInitDialog(){ CDialogEx::OnInitDialog();  // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) {  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);  // 设置小图标 // TODO: 在此添加额外的初始化代码 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); }  comboBox->SetCurSel(0); UpdateData(false); return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE}void CMulti_DisplayDlg::OnSysCommand(UINT nID, LPARAM lParam){ if ((nID & 0xFFF0) == IDM_ABOUTBOX) {  CAboutDlg dlgAbout;  dlgAbout.DoModal(); } else {  CDialogEx::OnSysCommand(nID, lParam); }}// 如果向对话框添加最小化按钮,则需要下面的代码//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,//  这将由框架自动完成。void CMulti_DisplayDlg::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 CMulti_DisplayDlg::OnQueryDragIcon(){ return static_cast<HCURSOR>(m_hIcon);}void CMulti_DisplayDlg::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 CMulti_DisplayDlg::SwitchPrimaryScreen(int newPrimary, int oldPrimary){ MoveNewPrimary(newPrimary, oldPrimary); MoveOldPrimary(newPrimary, oldPrimary); CommitChange();}void CMulti_DisplayDlg::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 CMulti_DisplayDlg::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 CMulti_DisplayDlg::CommitChange(){ ChangeDisplaySettingsEx (NULL, NULL, NULL, 0, NULL);}int CMulti_DisplayDlg::GetPrimaryScreen(){ return PrimaryNum;}int CMulti_DisplayDlg::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 CMulti_DisplayDlg::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 paths GetDisplayConfigBufferSizes(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 arrays PathArray = (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 information LONG 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 topology return 0;}int CMulti_DisplayDlg::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 CMulti_DisplayDlg::OnBnClickedOk(){ // TODO: 在此添加控件通知处理程序代码 SetPrimaryScreen(selIndex);  //CDialogEx::OnOK();}int count1 = 0;void CMulti_DisplayDlg::OnBnClickedButton1(){ // TODO: 在此添加控件通知处理程序代码 long lRet; selIndex = comboBox->GetCurSel();//取得选中的索引 if(selIndex < 0 )  return; count1++; if(count1 %2) {  lRet = ChangeScreenOrientation(selIndex, 1); } else {  lRet = ChangeScreenOrientation(selIndex, 0); }}int count = 0;void CMulti_DisplayDlg::OnBnClickedButton2(){ // TODO: 在此添加控件通知处理程序代码 count++; if(count %2) {  SetCloneView(SDC_TOPOLOGY_CLONE);  GetDlgItem(IDC_BUTTON2)->SetWindowTextW(_T("恢复")); } else {  SetCloneView(SDC_TOPOLOGY_EXTEND);  GetDlgItem(IDC_BUTTON2)->SetWindowTextW(_T("屏幕复制")); }}void CMulti_DisplayDlg::OnCbnSelchangeCombo1(){ // TODO: 在此添加控件通知处理程序代码 //取得选中的值 CString selStr; selIndex = comboBox->GetCurSel();//取得选中的索引 //comboBox->GetLBText(nIndex,selStr); //MessageBox(selStr);}void CMulti_DisplayDlg::OnCbnDropdownCombo1(){ // TODO: 在此添加控件通知处理程序代码 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);}