DosBox在winserver2016云桌面最大化界面无法铺满全屏的问题剖析

news2024/11/28 12:35:04

现象引出和问题猜想

     有一款用户软件叫DosBox,在实体机win11的时候最大化的时候,程序界面可以铺满全屏,但是在winserver2016云桌面进行最大化的时候,最大化的时候,界面无法铺满全屏:

(实体机最大化,界面全屏显示)

(云桌面程序最大化主界面无法铺满全屏)

这到底是什么原因导致在云桌面下程序最大化就无法铺满全屏呢?首先我们猜测,程序之所以能够铺满全屏是因为程序在最大化的时候设置了桌面的显示分辨率,即最大化的时候把桌面的分辨率设低,退出最大化的时候,恢复桌面原来的分辨率。通过代码:

void GetCurrentResolution_imp()
{
	DISPLAY_DEVICE DispDev;
	Res temp;
	for (auto iDevIdx = 0; iDevIdx < 2; iDevIdx++)
	{
		ZeroMemory(&DispDev, sizeof(DISPLAY_DEVICE));
		DispDev.cb = sizeof(DISPLAY_DEVICE);
		int dspNum = GetSystemMetrics(SM_CMONITORS);	// 两个显示器为复制模式时,仅在1/2上显示时,值都为1
		// 获取有关系统中显示设备的信息
		if (EnumDisplayDevices(NULL, iDevIdx, &DispDev, 0))
		{
			DEVMODE devMode;
			int display_id = 0;
			if ((DispDev.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
				continue;
			if ((DispDev.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) == DISPLAY_DEVICE_PRIMARY_DEVICE
				|| dspNum == 1)				// 主显示器或者只在一个显示器显示
			{
				display_id = 0;
			}
			else
			{
				display_id = 1;
			}
			if (EnumDisplaySettings(DispDev.DeviceName, ENUM_CURRENT_SETTINGS, &devMode))
			{
				temp.dmPelsHeight = devMode.dmPelsHeight;
				temp.dmPelsWidth = devMode.dmPelsWidth;
				temp.display_id = display_id;
				printf("w %d,h %d display_id %d\n", devMode.dmPelsWidth, devMode.dmPelsHeight, display_id);
			}
		}
	}
}

 不断轮询获取桌面的分辨率,发现在实体机下程序最大化的时候确实将桌面的分辨率调低了:

(实体机原来桌面的分辨率是3456*2160,程序最大化的时候桌面分辨率被修改为640*480)

 同样发现在云桌面下,DosBox同样把桌面分辨率调低了:

 (云桌面原来桌面的分辨率是1920*1080,程序最大化的时候桌面分辨率被修改为1024*768)

   这就发现可能是这两个系统可设置分辨率的最小值不一样,云桌面的可设置分辨率的最小值比较大,导致了,界面无法被拉伸显示很大。看看两台系统的可设置分辨率:

(实体机win11可设置最小分辨率为800*600 )

(云桌面可设置最小分辨率为1024*768)

 可以发现实体机win11可设置最小分辨率为800*600,DosBox设置的分辨率为:640*480

云桌面server2016可设置最小分辨率为1024*768,DosBox设置的分辨率为1024*768

于是我们猜测,因为两台系统的可设置最小分辨率不一样,导致云桌面设置的最小分辨率太大导致DosBox界面无法放的足够大来覆盖桌面;但是同时发现在实体机下设置的分辨率并不是系统可设置的最小分辨率,设置的640*480比系统可设置最小分辨率800*600低。

跟踪剖析问题

设置系统分辨率一般是通过ChangeDisplaySettings这个api:

void changeResolution()
{
	DEVMODE DevMode;
	EnumDisplaySettings(NULL, 0, &DevMode);
	DevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
	DevMode.dmPelsWidth = 1920;
	DevMode.dmPelsHeight = 1200;
	DevMode.dmDisplayFrequency = 30;
	DevMode.dmBitsPerPel = 32;
	ChangeDisplaySettings(&DevMode, 0);
}

但是通过apimonitor进行api拦截,发现并未拦截到该api的调用,猜测程序并不是通过调用ChangeDisplaySettings来设置桌面分辨率的。把Doxbox.exe拖拽到ida上:

看到DosBox.exe导入表并没有导入ChangeDisplaySettings这个api,而且apimonitor也确实没抓取到程序对该api的调用,但是发现导入表导入了较多的opengl的函数。于是猜测程序是通过opengl的api来修改系统的分辨率的,通过百度我也发现了一篇文章:

确实发现有人通过SDL_SetVideoMode来设置桌面的分辨率,而且这个函数也是DoxBox调用过的。

但是通过ida看伪码发现他们调用方式并不一样:

 SDL_SetVideoMode这个函数调用的地方也比较多的:

 把SDL.dll拖拽到ida上,直接调试:

传入参数 a1(0x280) a2(0x190)应该是传入的需要设置的宽640*400,这个跟网络blog上对setvideomode函数调用一致,前两个参数分别是宽和高 。

 

 通过调试发现程序是通过SDL_ListMode来获取可设置分辨率的列表,最后获取到最小的分辨率数组下标,最后得到v110为0x280,v109为0x1E0,即为宽640 高480,即这个就是最后设置的桌面分辨率,但是奇怪,系统可设置分辨率的列表并没有640*480,最小是800*600。那么就要分析SDL_ListModes这个函数了。

我在网络上找到这个api的用法:

 然后到 http://www.libsdl.org/release/SDL-1.2.15/ (注意是这个版本,新版本在github上已经没有了这个api)下载源代码并编译。

我在源代码找到SDL_SetVideoMode这个函数,发现这个跟我在ida上调试的代码大致一致(因为SDL版本不同,所以源码还是有区别的):

 

 于是我写了测试代码,参数传入ida上传递的参数,然后进行调试:

 调试到SDL_ListModes这个api:

发现获取分辨率列表就是通过获取SDL_modelist这个列表获取的,而这个SDL_modelist数组是怎么获取的呢,我在代码里面全局搜索看看该数组的引用,并发现一个函数DIB_AddMode:

 好,应该就是在这里去把分辨率列表添加进去的,那到底是哪里调用的呢,打上断点顺藤摸瓜,重启程序进行调试,最后断点断在我打的断点下,看下调用堆栈:

 好了,原来是在DIB_VideoInit这个api然后调用EnumDisplaySettings这个win api来获取系统桌面分辨率的了。

通过demo代码枚举出桌面的分辨率:

 运行demo程序:

 发现DosBox设置的屏幕分辨率其实就是枚举的最小分辨率,这个枚举的分辨率跟系统设置的分辨率是有差异的,并不是期初认为的一致的。

同时在DIB_SetVideoMode这个函数实现找到了改变分辨率的实现:

 

最后在apimonitor也抓取到了对ChangeDisplaySettingsExA(可能是之前版本调用的是这个api,所以api有所区别)的调用:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/512849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

递归到动态规划:省去枚举行为

如果在动态规划的过程中没有枚举行为&#xff0c;那严格位置依赖和傻缓存的方式并没有太大区别&#xff0c;但是当有枚举行为的时候&#xff08;一个位置依赖于多个位置&#xff09;&#xff0c;那严格位置依赖是有优化空间的&#xff0c;枚举行为也许可以省去&#xff0c;题目…

Docker(一)

Docker Docker简述 传统虚拟机技术基于安装在主操作系统上的虚拟机管理系统&#xff0c;创建虚拟机&#xff08;虚拟出各种硬件&#xff09;&#xff0c;在虚拟机上安装从操作系统&#xff0c;在从操作系统中安装和部署各种应用。这种方式占用资源很大并且步骤冗余。在此基础之…

跟着LearnOpenGL学习2--三角形绘制

文章目录 一、前言二、图形渲染管线2.1、顶点数据2.2、顶点着色器2.3、形状&#xff08;图元&#xff09;装配2.4、几何着色器2.5、光栅化2.6、片段着色器2.7、测试与混合 三、渲染流程3.1、顶点输入3.2、顶点着色器3.3、编写、编译着色器3.4、片段着色器3.5、着色器程序3.6、链…

专业专注,极致体验,高端隐形智能晾衣机品牌邦先生官宣浙江卫视知名主持人沈涛为品牌代言人

5月11日&#xff0c;高端隐形晾衣架领导品牌邦先生正式宣布&#xff0c;浙江卫视知名主持人沈涛为品牌代言人&#xff0c;以更高标准的晾晒&#xff0c;共同迎接智能晾晒大时代&#xff0c;用科技力量创造美好智慧家居生活。 专业实力品牌邦先生王牌主持沈涛 作为浙江卫视的“王…

GPT Prompt(提示词)写法与教程,相关站点与工具

文章目录 1、Prompt工程师&#xff08;提示工程师&#xff09;2、提示词教程3、提示词工具&#xff08;中文&#xff09;4、提示词工具&#xff08;英文&#xff09; 1、Prompt工程师&#xff08;提示工程师&#xff09; Prompt工程师&#xff0c;也称为AI提示工程师&#xff…

纸质文件怎么扫描成电子版?简单小妙招快来拿捏

随着科技的发展&#xff0c;越来越多的人将纸质文件转换为电子版&#xff0c;以方便存储和共享。本文将介绍纸质文件如何扫描成电子版&#xff0c;以及如何利用手机进行扫描转换。 纸质文件扫描成电子版 将纸质文件扫描成电子版是一种常见的方式。首先&#xff0c;您需要一台扫…

JAVA-代码块和内部类

文章目录 目录 文章目录 前言 1.代码块 1.1什么是代码块? 1.2代码块的分类及作用: 1.静态代码块 2.成员代码块(又叫做构造代码块) 3.局部代码块 2.内部类 2.1 什么是内部类? 2.2 内部类的分类 1.成员内部类 2.静态内部类 3.匿名内部类 4.局部内部类 总结 前言 作者简介:我是最…

MySQL索引优化(超详细)

Mysql索引优化 1 索引介绍 1.1 什么时MySQL的索引 ​ MySQL官方对于索引的定义:索引是帮助MySQL高效获取数据的数据结构。 ​ MySQL在存储数据之外&#xff0c;数据库系统中还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种引用(指向)表中的数据&#xff…

Prometheus

Prometheus简介 prometheus是一个监控、告警的开源系统。Prometheus收集并存储时序的指标数据。指标数据存储伴随一个timestamp和可选择key-values 队列标签 Prometheus特性&#xff1a; 一个时序的多维数据模型&#xff0c;被mertic name和 key/value pairs标签唯一定义 P…

将DenseNet换成Resnet——更换深度学习骨干网络

最近我在学习一个手写公式识别的网络&#xff0c;这个网络的backbone使用的是DenseNet&#xff0c;我想将其换成ResNet 至于为什么要换呢&#xff0c;因为我还没换过骨干网络&#xff0c;就像单纯拿来练练手&#xff0c;增加我对网络的熟悉程度&#xff0c;至于会不会对模型的性…

【时间序列数据挖掘】ARIMA模型

目录 0、前言 一、移动平均模型MA 二、自回归模型AR 三、自回归移动平均模型ARMA 四、自回归移动平均模型ARIMA 【总结】 0、前言 传统时间序列分析模型&#xff1a; ARIMA模型是一个非常灵活的模型&#xff0c;对于时间序列的好多特征都能够进行描述&#xff0c;比如说平…

5.11黄金最新行情走势分析及多空交易策略

近期有哪些消息面影响黄金走势&#xff1f;本周黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;北京时间周三(5月10日)20:30&#xff0c;美国劳工部公布4月通胀报告&#xff0c;整体与核心CPI年率都走低&#xff0c;支持美联储6月份保持利率不变。数据显示&…

RabbitMQ详解(一):Linux安装

消息队列概念 消息队列是在消息的传输过程中保存消息的容器。队列的主要目的是提供路由并保证消息的传递&#xff1b;如果发送消息时接收者不可用&#xff0c;消息队列会保留消息&#xff0c;直到可以成功地传递它。 常见的消息队列 RabbitMQ 基于AMQP(高级消息队列协议)基础上…

不要轻易放弃丢失的U盘文件夹数据,这里有按文件夹恢复数据的技巧

U盘&#xff0c;全名叫USB闪存盘&#xff0c;是一种便携式的存储设备&#xff0c;是一种可以插入到电脑等电子设备上进行数据传输和存储的硬件设备。U盘的使用方便、速度高、存储容量大、稳定性高&#xff0c;因此被广泛用于数据备份、文档传输、音频视频存储等方面。但是使用过…

easyrecovery免费版2023最新电脑数据恢复软件

通常&#xff0c;许多人会将工作或生活中的数据存储在我们的计算机上。很多时候&#xff0c;由于我们的误操作或其他一些问题&#xff0c;很容易错误地删除一些文件和数据。特别是&#xff0c;一些计算机故障总是会导致数据丢失&#xff0c;这是非常麻烦的。当需要重新安装系统…

【TA100】5 纹理的秘密

1 是什么&#xff1f; 2 为什么使用纹理 3 纹理管线 纹理投影 展开UV到UV坐标系 模型坐标> uv坐标 > 乘分辨率(256 256) > 颜色采样 4 纹理模式 重复&#xff0c;镜像重复&#xff0c;边界拉伸&#xff0c;填充颜色 5 采样模式 它决定了当纹理由于变换而产生拉伸时&a…

go小技巧(易错点)集锦

目录 len的魔力评论区大佬解答答案详解 结构体是否相等答案解析&#xff1a;结构体比较规则举例 常量的编译我的答案标准答案内存四区概念&#xff1a; new关键字答案 iota的魔力结果解析可跳过的值定义在一行中间插队 小结iota详解iota 原理iota 规则依赖 const按行计数多个io…

云数据库技术沙龙|多云多源下的数据复制技术解读-NineData

摘要&#xff1a;随着数据智能时代的到来&#xff0c;多云多源架构下的数据管理是企业必备的基础设施&#xff0c;我们认为数据存取、数据集成与分发、数据安全与数据质量是基础&#xff0c;也是走向多云多源架构的起点。本议题介绍云原生的多云多源数据管理NineData&#xff0…

PlSql存储过程基础

目录儿 常用指令1. 什么是PLSQL语言2. PLSQL程序结构2.1 第一个程序 HelloWord:2.2 执行程序2.2.1 在工具中执行2.2.2 在sqlplus客户端中执行(命令行) 3. 变量3.1 普通变量3.2 引用型变量3.3 记录型变量 4. 流程控制4.1 条件分支4.2 循环 5. 游标5.1 定义5.2 语法5.3 游标的属性…

或许你需要这套uni-app打包android与ios流程

1、hbuilder每个账户的每日云打包有上限次数限制&#xff0c;超出次数要么换账户要么换成本地打包(uni-app提供了足够多云端的打包次数) 2、android打包&#xff0c;也就是apk包 优先搞明白两个需求&#xff1a; 、android包名是否为默认值&#xff0c;如果是默认值&#xf…