VC++编程实现多显示器控制(复制、横屏、纵屏,显示器个数)

最近做了个三维的程序,部署到客户机器上,客户看了后,现场提出这样的一个需求:程序能智能探测接入的显示器个数,当有新的显示器接入时,现有的只在一个显示器上显示的三维场景能投递到新插入的显示器上显示。类似在桌面上点击鼠标右键,选择“显示设置”菜单,弹出的如下界面:

VC++对话框工程代码实现如下:

.h文件如下:


// displayOperDlg.h: 头文件
//

#pragma once
#include<list>
using std::list;

// CDisplayOperDlg 对话框
class CDisplayOperDlg : public CDialogEx
{
// 构造
public:
	CDisplayOperDlg(CWnd* pParent = nullptr);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_DISPLAYOPER_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持

	void    GetAllMonitors();
	void    SwitchPrimaryScreen(int newPrimary, int oldPrimary);
	void    MoveOldPrimary(int newPrimary, int oldPrimary);
	void    MoveNewPrimary(int newPrimary, int oldPrimary);
	void    CommitChange();
	int    GetPrimaryScreen();
	int    SetPrimaryScreen(int num);
	int    SetCloneView(int mode);
	int    ChangeScreenOrientation(int num, int rotation);
// 实现
protected:
	HICON m_hIcon;

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnCbnSelchangeCombo1();
	afx_msg void OnCbnDropdownCombo1();
	afx_msg void OnBnClickedButton1();
	afx_msg void OnBnClickedButton2();
	afx_msg void OnBnClickedOk();

private:
	list<DISPLAY_DEVICE> dev_list;
	CComboBox* comboBox;
	list<DEVMODE>dev_mode_list;
	int PrimaryNum, selIndex, count1;
	int count{0};
};

.cpp文件如下:


// displayOperDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "displayOper.h"
#include "displayOperDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CDisplayOperDlg 对话框



CDisplayOperDlg::CDisplayOperDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DISPLAYOPER_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CDisplayOperDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CDisplayOperDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDOK, &CDisplayOperDlg::OnBnClickedOk)
	ON_CBN_SELCHANGE(IDC_COMBO1, &CDisplayOperDlg::OnCbnSelchangeCombo1)
	ON_CBN_DROPDOWN(IDC_COMBO1, &CDisplayOperDlg::OnCbnDropdownCombo1)
	ON_CBN_SELCHANGE(IDC_COMBO1, &CDisplayOperDlg::OnCbnSelchangeCombo1)
	ON_CBN_DROPDOWN(IDC_COMBO1, &CDisplayOperDlg::OnCbnDropdownCombo1)
	ON_BN_CLICKED(IDC_BUTTON1, &CDisplayOperDlg::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON2, &CDisplayOperDlg::OnBnClickedButton2)
	ON_BN_CLICKED(IDOK, &CDisplayOperDlg::OnBnClickedOk)
END_MESSAGE_MAP()


// CDisplayOperDlg 消息处理程序

BOOL CDisplayOperDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		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);		// 设置小图标

	GetAllMonitors();
	CComboBox* 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 CDisplayOperDlg::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 CDisplayOperDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CDisplayOperDlg::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 CDisplayOperDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CDisplayOperDlg::SwitchPrimaryScreen(int newPrimary, int oldPrimary)
{
	MoveNewPrimary(newPrimary, oldPrimary);
	MoveOldPrimary(newPrimary, oldPrimary);
	CommitChange();
}

void CDisplayOperDlg::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 CDisplayOperDlg::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 CDisplayOperDlg::CommitChange()
{
	ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
}

int CDisplayOperDlg::GetPrimaryScreen()
{
	return PrimaryNum;
}

int CDisplayOperDlg::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 CDisplayOperDlg::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 CDisplayOperDlg::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 CDisplayOperDlg::OnCbnSelchangeCombo1()
{
	//取得选中的值
	CString selStr;
	int selIndex = comboBox->GetCurSel();//取得选中的索引
	comboBox->GetLBText(selIndex,selStr);

	MessageBox(selStr);
}


void CDisplayOperDlg::OnCbnDropdownCombo1()
{
	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);
}


void CDisplayOperDlg::OnBnClickedButton1()
{
		long lRet;

	selIndex = comboBox->GetCurSel();//取得选中的索引
	if (selIndex < 0)
		return;
	count1++;
	if (count1 % 2)
	{
		lRet = ChangeScreenOrientation(selIndex, 1);
	}
	else
	{
		lRet = ChangeScreenOrientation(selIndex, 0);
	}
}


void CDisplayOperDlg::OnBnClickedButton2()
{
	count++;
	if (count % 2)
	{
		SetCloneView(SDC_TOPOLOGY_CLONE);
		GetDlgItem(IDC_BUTTON2)->SetWindowTextW(_T("恢复"));
	}
	else
	{
		SetCloneView(SDC_TOPOLOGY_EXTEND);
		GetDlgItem(IDC_BUTTON2)->SetWindowTextW(_T("屏幕复制"));
	}
}


void CDisplayOperDlg::OnBnClickedOk()
{
	SetPrimaryScreen(selIndex);
	//
//	CDialogEx::OnOK();
}

 参考链接:

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

附上几个有用的链接:

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

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

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值