前言
本篇主要是分享win10及国产桌面操作系统(统信UOS、麒麟kylin系统)的窗口的特性(窗口消息、窗口ID、窗口状态等),及一些功能开发技术点或者是思路(本文的风格是零散的方式分享技术点,不提供源码,仅提供一个编译好的获取到窗口消息的应用)。
背景简述
信创项目,主要是让用户使用国产化的硬件终端(国产CPU,国产内存等),以及国产操作系统(统信UOS、麒麟kylin系统、欧拉系统等),因为甲方存在很多外设设备无法在Linux中使用(时间太久,厂商不提供Linux下的驱动,比如一台老的打印机,只能在window下使用),那么就需要一个过渡阶段(在Linux中操作window的业务应用),直到业务应用全部转移到国产操作系统为止。
linux/window之间的窗口应用同步虚拟化方案,是过渡阶段比较主流的成熟方案,比如统信UOS提供的彩虹平台(收费),以及各个瘦客户端厂商自研的方案,都是此类方案为主。
窗口应用分享
window系统
窗口工具
spy++
安装有VC++ 6.0,可以在系统菜单中找到
安装有VS2022的话,可以在IDE的工具菜单中找到
前面数值是窗口句柄ID,后面的类名称,如下图所示:
包含了很多窗口句柄,但特殊的“窗口”是获取不到的。这里分享一个坑点,系统托盘并不是一个独立窗口,而是一个button(区域),可以通过TrayNotifyWnd/Button获取到系统托盘的数量,但获取不到具体的系统托盘句柄及信息,因为这些button(区域)是系统托盘区域绘画上去的,无法直接获取。
坐标提取工具
需要鼠标模拟操作时,用得上这个工具
应用开发
涉及到底层的window API,本人使用VS2022以及QT的MSVC编译器版本进行的开发,常用接口如下:
findWindow()是根据名称获取窗口句柄;
GetParent()通过窗口句柄获取父窗口句柄;
GetWindowThreadProcessId()通过句柄获取进程ID;
GetWindowText()通过窗口句柄获取窗口标题;
GetWindowLongPtr()通过窗口句柄获取窗口样式(判断是否为可见的窗口);
GetClassName()通过窗口句柄窗口类名;
showWindow()通过句柄让窗口显示(最小化到任务栏、显示到桌面等);
SendMessage()向指定的窗口发送事件消息(比如关闭窗口WM_CLOSE);
PostMessage()向指定的窗口发送事件消息(比如关闭窗口WM_CLOSE);
窗口属性
应用窗口中还包含有:对话框、工具框(在任务栏上没有应用图标)等特征。
比如通用对话框的窗口类名中包含"#32770"
让任务栏透明化
本人调研过注册表或者第三方自定义主题工具让任务栏透明/隐藏,效果不如以下代码
void TaskBarWnd::SetTaskbarTransparency(int alpha) {
HWND hTaskbar = FindWindow(L"Shell_TrayWnd", NULL);
if (hTaskbar) {
// 启用窗口透明
SetWindowLong(hTaskbar, GWL_EXSTYLE,
GetWindowLong(hTaskbar, GWL_EXSTYLE) | WS_EX_LAYERED);
// 设置透明度 (0-255)
//alpha>0时,值越小,越透明,鼠标可以正常点击任务栏
//alpha为0时,任务栏消失,鼠标无法点击到任务栏
SetLayeredWindowAttributes(hTaskbar, 0, alpha, LWA_ALPHA);
// 禁用DWM过渡动画
BOOL disableTransitions = TRUE;
DwmSetWindowAttribute(hTaskbar,
DWMWA_TRANSITIONS_FORCEDISABLED,
&disableTransitions,
sizeof(disableTransitions));
}
}
监测桌面指定的“图片”
如果需要监测第三方应用是否出现指定的“图片”特征,比如一个唯一性按钮,需要进行点击操作,可以参考我另外的文章python pyautogui 捕捉桌面按钮,并进行点击操作_pyautogui 如何找按钮-CSDN博客
QT常用库主要是以下几个
LIBS += -lPsapi -lcomctl32 -ldwmapi -luser32 -lgdi32
Linux系统
窗口工具
xwininfo
查看窗口信息
xwininfo -id 0x0a400011
xwininfo: Window id: 0xa400011 "mytool : 1"
Absolute upper-left X: 163 //窗口左上角相对于屏幕左上角的绝对坐标(单位:像素)
Absolute upper-left Y: 29 //窗口左上角相对于屏幕左上角的绝对坐标(单位:像素)
Relative upper-left X: 0 //窗口内容区域相对于窗口边框的偏移量
Relative upper-left Y: 0 //窗口内容区域相对于窗口边框的偏移量
Width: 1681 //窗口内容区域的宽度
Height: 919 //窗口内容区域的高度
Depth: 24
Visual: 0x21
Visual Class: TrueColor
Border width: 0
Class: InputOutput //窗口类型为可交互的输入输出窗口
Colormap: 0x20 (installed)
Bit Gravity State: NorthWestGravity
Window Gravity State: NorthWestGravity
Backing Store State: NotUseful
Save Under State: no
Map State: IsViewable //窗口当前可见且未被最小化---无效值
Override Redirect State: no
Corners: +163+29 -76+29 -76-132 +163-132 //窗口四个角相对于屏幕的坐标(格式为+X+Y或-X-Y)
-geometry 1681x919-76+29
以上主要有用的值是X、Y、Width 和 Height,原本Map State是表示显示/隐藏的,结果测试这个值一直没有变化。
以上命令行涉及了窗口ID,以下讲解两种窗口ID获取方式:
1、QT获取窗口ID的方法
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
WId windowId = this->winId();
//转换成字符串
QString hwndid = QString("0x%1").arg(windowId, 8, 16, QLatin1Char('0'));
}
2、命令行获取方法:wmctl -x -l (下面详细介绍这个系统命令)
wmctl
第一列(例如:0x0c000006,标识为CLASSID)是当前的窗口句柄,第三列(peony-qt-desktop.桌面,标识为CLASS)是窗口类,第四列(xxxx 桌面,标识为win_name)是应用窗口的名称。
应用场景:把指定窗口激活显示到桌面
wmctrl -i -r <CLASSID> -b remove,hidden && wmctrl -i -a <CLASSID>
xprop
查看窗口状态,实时窗口状态,可以用以下命令行
xprop -id 0x05a000fc -spy
返回的值及说明如下:
_NET_WM_STATE_MAXIMIZED_HORZ:表示窗口处于水平方向最大化状态(宽度填满屏幕)
_NET_WM_STATE_HIDDEN:表示窗口当前处于最小化/隐藏状态(即缩小到任务栏)
_NET_WM_STATE_ABOVE:窗口层级属性,表示该窗口被设置为始终显示在其他窗口之上
_NET_WM_STATE_STAYS_ON_TOP:窗口置顶属性,即使最小化后恢复仍保持最顶层显示
_NET_WM_STATE_MAXIMIZED_VERT:表示窗口处于垂直方向最大化状态(高度填满屏幕)
_NET_WM_STATE_FOCUSED:表示窗口当前获得键盘焦点的属性标志
应用开发
Linux对窗口的操作主要是x11(X Window System Version 11),QT项目引入时,需要在.pro文件中加入以下内容
QT += x11extras
LIBS += -lx11 -lxcb
虽然有开发库,但国产的桌面操作系统(统信UOS、麒麟kylin),界面交互都是高度定制的,x11lib不是很适合作为开发库。
比如我需要让任务栏上的应用窗口隐藏起来,尝试了很多种方式,全部无效。但是我卸载系统自带的任务栏,安装上开源的第三方任务栏,遵循x11标准的是可以隐藏的。
比如我需要屏蔽win+D快捷键,x11库是支持捕获键盘的,但是唯独快捷键无法捕获。这些都是国产系统高度定制导致,所以后台命令行反而更适合项目开发选型。
监测窗口是否在桌面显示
写一个定时器,通过xprop命令监测指定窗口是否包含属性:_NET_WM_STATE_HIDDEN
xprop -id 0x05a000fc|grep "_NET_WM_STATE_HIDDEN"
让窗口在桌面置顶显示
通过wmctrl进行窗口操作
wmctrl -i -r <CLASSID> -b remove,hidden && wmctrl -i -a <CLASSID>
实际用户场景中,用户会在桌面使用crtl+D,或者点击任务栏最右边的边界处,会导致桌面显示的窗口都被强制最小化,如果开发的是工具型应用(不在任务栏中显示窗口图标),用户是无法再找到窗口的,此时结合以上两个特性,可以让应用一直显示在桌面。