1. 程式人生 > >VC++實現Windows中雙顯示器(主屏、擴充套件屏)各種操作的原始碼工程

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

附上幾個有用的連結: