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

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


// 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);
}



附上几个有用的链接:

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


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






你可能感兴趣的:(windows,分辨率,方向,双屏,扩展屏,主屏,屏幕复制)