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
// 使图标在工作区矩形中居中
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
}
void CMulti_DisplayDlg::GetAllMonitors()
{
std::list
std::list
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
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
for (it1 = dev_list.begin(); it1 != dev_list.end(); it1++)
{
if (index == oldPrimary)
break;
index++;
}
index = 0;
std::list
for (it2 = dev_mode_list.begin(); it2 != dev_mode_list.end(); it2++)
{
if (index == newPrimary)
break;
index++;
}
index = 0;
std::list
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
for (it1 = dev_list.begin(); it1 != dev_list.end(); it1++)
{
if (index == newPrimary)
break;
index++;
}
index = 0;
std::list
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
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
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