C++实现显示(监视)器复制显示

1.背景:

设备上的应用启动后正常运行,当有显示器接入到设备的视频输出口(HDMI/DP)时,要求将主屏的内容复制到新接入的显示器上进行显示,且分辨率和显示方向保持主屏一致。

2.调研和思路:

经过查找资料(有效的博客)及调试验证:

C++编程实现多显示器控制(复制、横屏、纵屏,显示器个数)等_荆楚闲人的博客-CSDN博客_c++ 多显示器需求的提出: 最近做了个三维的程序,部署到客户机器上,客户看了后,现场提出这样的一个需求:程序能智能探测接入的显示器个数,当有新的显示器接入时,现有的只在一个显示器上显示的三维场景能投递到新插入的显示器上显示。类似在桌面上点击鼠标右键,选择“显示设置”菜单,弹出的如下界面:然后在此界面,对显示器进行复制、横向、纵向等处理。可以在程序中开启一个线程,进行探测,探测到显示器个数增加后,调用SetDisplayConfig(0, NULL, 0, NULL, SDC_TOPOLOGY_CLONE.https://blog.csdn.net/danshiming/article/details/112554980

Windows 下编程检测显示器信息及插拔_shallen320的博客-CSDN博客该文章讨论了windows下编程获得显示器信息,并检测显示器硬件插入拔出的方法。https://blog.csdn.net/shallen320/article/details/45070477

确定思路:

1>获取当前设备主屏的设备以及显示模式;

2>监控显示器的拔插事件,根据事件对显示设备和监视器的信息进行读取,然后做相应的处理:

设置克隆模式,并将显示设置模式和主屏的模式一致。

3.解决方案:

1>获取当前设备主屏的设备以及显示模式;

关键函数:EnumDisplayDevices:查找到主显示设备;

                  EnumDisplaySettings:获取显示设备的显示模式:分辨率,方向等等。

                  SetDisplayConfig:设置显示设备
                 ChangeDisplaySettingsEx修改显示设置,方向等。

                EnumDisplayMonitors:查找监视器的信息,主要获取了数量和多显示器的显示模式。该函数采用回调实现,我们需要实现自己的回调函数,回调函数如下:

BOOL CALLBACK EnumDisplayDeviceProc(
	HMONITOR hMonitor,
	HDC hdcMonitor,
	LPRECT lprcMonitor,
	LPARAM dwData
)
{
	MONITORINFOEX mi;
	ZeroMemory(&mi, sizeof(mi));
	mi.cbSize = sizeof(mi);
	GetMonitorInfo(hMonitor, &mi);
	g_monitorRlt= GetMonitorInfoRlt(hMonitor);
	return g_monitorRlt.isCloneSettting;
}

其中GetMonitorInfoRlt为我们的具体功能函数,可根据不同的需求做对应的更改。该功能函数中主要使用GetMonitorInfoW,GetDisplayConfigBufferSizes,QueryDisplayConfig来获取监视器数量和显示配置。

tMonitorRlt GetMonitorInfoRlt(HMONITOR monitor)
{
	tMonitorRlt rlt;
	rlt.isCloneSettting = false;
	rlt.numbers = 0;

	DISPLAYCONFIG_TOPOLOGY_ID currentTopologyId;
	MONITORINFOEXW info;
	info.cbSize = sizeof(info);
	GetMonitorInfoW(monitor, &info);

	UINT32 requiredPaths, requiredModes;
	GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &requiredPaths, &requiredModes);
	std::vector<DISPLAYCONFIG_PATH_INFO> paths(requiredPaths);
	std::vector<DISPLAYCONFIG_MODE_INFO> modes(requiredModes);
	QueryDisplayConfig(QDC_DATABASE_CURRENT, &requiredPaths, paths.data(), &requiredModes, modes.data(), &currentTopologyId);

	rlt.isCloneSettting = currentTopologyId == DISPLAYCONFIG_TOPOLOGY_CLONE ? true : false;
	rlt.numbers = paths.size();
	return rlt;
}

2>监控显示器的拔插事件

当显示器拔插后,Windows系统的设备和打印机页面就会有显示器的图标显示和消失,同时还显示了显示器的名字。所以是可以通过监听Windows的消息来获取拔插事件的。

通过查找资料发现,Windows对外部设备都有统一的GUID限制。通过注册GUID设备的通知消息,就可以在代码中实现拔插消息的相应。

有效资料连接:

GUID获取

GUID_DEVINTERFACE_MONITOR - Windows drivers | Microsoft Docshttps://msdn.microsoft.com/en-us/library/windows/hardware/ff545901(v=vs.85).aspx事件监控示例:

csdn - 安全中心https://link.csdn.net/?target=https%3A%2F%2Fmsdn.microsoft.com%2Fen-us%2Flibrary%2Fwindows%2Fdesktop%2Faa363432%2528v%3Dvs.85%2529.aspx具体实现如下:

1.注册监视器的GUID,用于监控其操作信息,函数如下

GUID WceusbshGUID = { 0xE6F07B5F,0xEE97,0x4a90,0xB0,0x76,0x33,0xF5,0x7B,0xF4,0xEA,0xA7 };
    if (DoRegisterDeviceInterfaceToHwnd(
        WceusbshGUID,
        (HWND)this->winId(),
        &m_hDeviceNotify))
    {
        m_pMonitorSet = new CMonitorSet;
    
    }

BOOL DoRegisterDeviceInterfaceToHwnd(
	IN GUID InterfaceClassGuid,
	IN HWND hWnd,
	OUT HDEVNOTIFY* hDeviceNotify
)
{
	DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;

	ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
	NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
	NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
	NotificationFilter.dbcc_classguid = InterfaceClassGuid;

	*hDeviceNotify = RegisterDeviceNotification(
		hWnd,                       // events recipient
		&NotificationFilter,        // type of device
		DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
	);

	if (NULL == *hDeviceNotify)
	{

		return FALSE;
	}

	return TRUE;
}

2.Qt的窗口类继承QAbstractNativeEventFilter,并重载bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);使用installNativeEventFilter安装事件过滤。

3.在bool nativeEventFilter(const QByteArray &eventType, void *message, long *result)实现拔插后的函数处理:

bool rlt = EnumDisplayMonitors(NULL, NULL, EnumDisplayDeviceProc, 0);
	if (!rlt)
	{
		if (m_primaryDeviceMode.dmDisplayOrientation == DMDO_DEFAULT)
		{
			m_primaryDeviceMode.dmDisplayOrientation = DMDO_90;
			DWORD dwTemp = m_primaryDeviceMode.dmPelsHeight;
			m_primaryDeviceMode.dmPelsHeight = m_primaryDeviceMode.dmPelsWidth;
			m_primaryDeviceMode.dmPelsWidth = dwTemp;
		}

		SetDisplayConfig(0, NULL, 0, NULL, SDC_TOPOLOGY_CLONE | SDC_APPLY);
		
			ChangeDisplaySettingsEx(m_primaryDisplayDevice.DeviceName,
				&m_primaryDeviceMode,
				NULL,
				CDS_UPDATEREGISTRY,
				NULL);
		
	}

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
串口监视工具是一种用于监控串口通信的软件工具,它可以用于实时监控串口接收和发送的数据,提供了数据显示、分析和记录功能。 首先,我们需要选择合适的开发平台和编程语言来实现串口监视工具。常用的开发平台包括Windows、Linux和嵌入式系统,而编程语言可以选择C、C++、Python等。 接下来,我们需要使用串口编程库来进行串口通信的操作。在C语言中,可以使用类似Windows API中的CreateFile函数打开串口设备,使用ReadFile函数读取串口接收的数据,使用WriteFile函数发送数据到串口。此外,还可以使用SetCommState函数设置串口的波特率、数据位、停止位等参数,使用PurgeComm函数清除串口缓冲区。 然后,我们需要设计界面来显示串口通信的数据。可以使用图形界面库(如Qt)或命令行界面实现。界面包括显示接收和发送的数据,可以展示成十六进制或ASCII码形式,同时可以显示数据的时间戳、发送/接收状态等信息。 进一步,我们还可以添加数据分析和记录功能。例如,可以对接收的数据进行校验和解析,判断数据是否符合特定格式或协议。同时,还可以记录通信过程中的日志,方便调试和故障排查。 最后,在开发完成后,需要进行充分的测试,包括对各种情况下的串口通信进行测试,如高频率数据发送、大数据量传输、错误处理等。在测试过程中,需要验证工具的稳定性、性能和可靠性。 总而言之,实现串口监视工具需要选择合适的开发平台和编程语言,使用串口编程库进行通信操作,设计界面来显示和分析数据,并进行充分的测试以保证工具的正常运行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值