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