当前位置: 首页 > news >正文

处理窗口的常用API函数及窗口处理经验总结(附源码)

目录

1、检测窗口状态

2、将窗口前置显示

2.1、将窗口拉到最前面显示

2.2、将窗口置顶显示

2.3、将窗口设置到指定窗口的上面

3、将不显示的窗口强行显示出来

4、获取窗口的信息

5、通过窗口信息去查找窗口

5.1、调用GetClassName接口去比对窗口的类名

5.2、调用FindWindow去查找指定窗口类名和标题的窗口

5.3、通过给窗口设置属性值去标记窗口或者传递标记信息

6、调用SetWindowLong给目标窗口设置新的窗口处理函数,在新窗口处理函数中拦截消息


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N176https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N176https://blog.csdn.net/chenlycly/article/details/125529931       在GUI用户界面用户程序中,需要调用系统API函数去操作并控制窗口对象,下面对常用的API函数及窗口处理经验做一个总结,以供参考。

1、检测窗口状态

       比如判断是否是有效的窗口、窗口是否处于显示状态、窗口是否最小化或最大化等,用到的API接口都是比较常用的。

       判断是否是个有效的窗口,调用IsWindow(传入指向窗口的窗口句柄),主要用来检测窗口句柄指向的窗口是否已经被关闭(销毁)。有时我们在操作窗口或者给窗口发送消息之前,会去检测窗口句柄是否有效。

       判断目标窗口是否处于掩藏状态(非显示状态),调用IsWindowVisible接口。判断窗口是否最小化,调用IsIconic。判断窗口是否最大化,调用IsZoomed。

2、将窗口前置显示

       有时我们需要将窗口拉到最前面显示或者置顶显示,或者是指定窗口的Z序让某个窗口显示另一个窗口上面。

2.1、将窗口拉到最前面显示

        有时我们在创建好窗口后,需要将窗口拉到最前端显示,比如:

// 窗口已经创建
ShowWindow( hWnd, SW_SHOW );
SetForegroundWindow( hWnd );

       有时我们点击界面中的某个按钮去将某个窗口显示出来,这个窗口之前已经创建并打开,只是被其他窗口遮住了,这时我们只需要将目标窗口拉到最前面显示,就可以调用SetForegroundWindow。

2.2、将窗口置顶显示

       有时我们需要将目标窗口置顶显示,即将目标窗口显示在所有窗口最上面,始终显示在最上面不被其他窗口遮盖,则需要调用SetWindowPos接口,传入HWND_TOPMOST参数,如下所示:

::SetWindowPos(hTargetWnd, HWND_TOPMOST, NULL, NULL, NULL, NULL, SWP_NOSIZE | SWP_NOMOVE);

最后的参数标记,SWP_NOSIZE表示执行SetWindowPos时不改变窗口的大小,SWP_NOMOVE表示执行SetWindowPos时不改变窗口的位置。

       也可以将置顶窗口取消置顶,传入HWND_NOTOPMOST参数即可,如下:

::SetWindowPos(hTargetWnd, HWND_NOTOPMOST, NULL, NULL, NULL, NULL, SWP_NOSIZE | SWP_NOMOVE);

2.3、将窗口设置到指定窗口的上面

        这是将目标窗口的Z序固定到指定窗口的上面,可以调用SetWindowPos来实现:

::SetWindowPos(hTargetWnd, hWndInsertAfter, NULL, NULL, NULL, NULL, SWP_NOSIZE | SWP_NOMOVE);

将窗口句柄hTargetWnd指向的窗口的Z序设置到hWndInsertAfter指向的窗口上面。比如两个Z序同层次兄弟窗口,有着相同的父窗口,如果希望将一个固定显示在另一个窗口上面,就可以使用这个方法。

3、将不显示的窗口强行显示出来

       之前在开发新版本的软件时,正常情况下,程序启动后会把程序的主窗口显示出来,但在个别Win10的电脑上会时不时地出现程序启动后主窗口显示不出来的问题。这个问题在某几个电脑上不是必现的,但复现的概率很大。

       查看相关代码发现,主窗口已经创建并已经将主窗口给Show出来了,但在个别电脑上有时就是显示不出来,添加了调用SetForegroundWindow的代码,还是有问题。用VS自带的SPY++工具查看到窗口已经处于显示状态:

窗口风格中有WS_VISIBLE,表示窗口已处于显示状态。于是在SPY++抓取的窗口属性中查看窗口的坐标:

窗口坐标也是正常的,在Windows桌面可见范围内的!
       后来尝试采用规避的方法试试,强行将窗口拉出来显示。先尝试将窗口向左上角移动几个像素,再移动到原来的位置,代码如下:

HWND hTargetWnd;
RECT rcWnd;
::GetWindowRect(hTargetWnd, &rcWnd);// 1、先将窗口向左上角移动2个像素
RECT rcTmp = rcWnd;
rcTmp.left -= 2;
rcTmp.top -= 2;
rcTmp.right -= 2;
rcTmp.bottom -= 2;
MoveWindow(hTargetWnd, &rcTmp);// 2、再将窗口移动回原来的位置
MoveWindow(hTargetWnd, &rcWnd);

       经测试验证,这个方法确实是有效的,但有个小问题,因为来回移动窗口,窗口可能有个小抖动的感觉。于是又去尝试其他的方法,先将窗口置顶,然后再取消置顶,代码如下:

::SetWindowPos(hTargetWnd, HWND_TOPMOST, NULL, NULL, NULL, NULL, SWP_NOSIZE | SWP_NOMOVE);
::SetWindowPos(hTargetWnd, HWND_NOTOPMOST, NULL, NULL, NULL, NULL, SWP_NOSIZE | SWP_NOMOVE);

经测试,这个方法也是有效的,后来就采用了这个方法。

在实在排查不出具体的原因时,可以尝试去采用一些规避的办法去解决。

4、获取窗口的信息

       有时我们需要在程序中去识别一些窗口,看看窗口是否是目标窗口,通过获取一些窗口信息去鉴别。调用GetWindowText去获取窗口标题:

TCHAR szWndText[255] = {0};
GetWindowText(hTargetWnd, szWndText, sizeof(szWndText) / sizeof(TCHAR) - 1);
// 调用GetClassName去获取窗口类名:
TCHAR szWndClassName[255] = { 0 };
GetClassName(hTargetWnd, szWndClassName, sizeof(szWndClassName)/sizeof(TCHAR) - 1);

5、通过窗口信息去查找窗口

        可以通过窗口的一些属性信息去查找目标窗口,找到后对目标窗口进行操作。

5.1、调用GetClassName接口去比对窗口的类名

       注意,本文讲的窗口类名是注册窗口时指定的窗口类名,如下:

bool CXXXXXXWnd::RegisterWindowClass()
{WNDCLASS wc = { 0 };wc.style = GetClassStyle();wc.cbClsExtra = 0;wc.cbWndExtra = 0;wc.hIcon = NULL;wc.lpfnWndProc = CWindowWnd::__WndProc;wc.hInstance = CPaintManagerUI::GetInstance();wc.hCursor = ::LoadCursor( NULL, IDC_ARROW );wc.hbrBackground = NULL;wc.lpszMenuName  = NULL;wc.lpszClassName = _T("CTestDlg"); // 注册窗口时指定的窗口类名ATOM ret = ::RegisterClass(&wc);ASSERT( ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS );return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
}

并不是窗口对应的C++类的名称。

       比如如下的代码,将本进程中的窗口类名为CMenuWnd或CDiagnoseDlg的窗口自动关闭掉:

HWND hFocusWnd = ::GetFocus();
TCHAR szClassName[MAX_PATH] = { 0 };
::GetClassName(hFocusWnd, szClassName, sizeof(szClassName) / sizeof(TCHAR));
if (!_tcsicmp(szClassName, _T("CMenuWnd")) || !_tcsicmp(szClassName, _T("CDiagnoseDlg")))
{DWORD dwWndProcessId = 0;::GetWindowThreadProcessId(hFocusWnd, &dwWndProcessId);DWORD dwCurProcessId = ::GetCurrentProcessId();if (dwWndProcessId == dwCurProcessId) // 是本进程的窗口{// 直接将组合框的弹出的下拉窗口关闭掉::SendMessage(hFocusWnd, WM_CLOSE, 0, 0);}
}

代码中只处理本进程的相关窗口,所以调用GetWindowThreadProcessId获取窗口的进程id,和调用GetCurrentProcessId函数返回的当前进程id相比较。

        当然上述代码是非常规的做法,只用在特殊的场景下,这个地方只是给出一个示例。

5.2、调用FindWindow去查找指定窗口类名和标题的窗口

        FindWindow API函数的声明如下:

HWND WINAPI FindWindowW(__in_opt LPCTSTR lpClassName,
__in_opt LPCTSTR lpWindowName);

前一个参数是窗口类名,后一个参数是窗口名称,可以只通过其中一个搜索:(不用的参数置为NULL)

HWND hwnd = ::FindWindow( _T("窗口类名"), NULL);
// 也可以两个参数条件一起搜索:
HWND hwnd = ::FindWindow( _T("窗口类名"), _T("窗口名称");
if ( hwnd )
{::SendMessage( hwnd, WM_CLOSE, 0, 0 );
}

5.3、通过给窗口设置属性值去标记窗口或者传递标记信息

        可以调用SetProp API函数给目标窗口设置属性及属性值:

::SetProp(hTargetWnd, _T("Target_Paint_Wnd_Property"), (HANDLE)1);

给目标窗口添加Target_Paint_Wnd_Property属性,并将属性值设置为1。这样,在搜索窗口时,可以去读窗口的Target_Paint_Wnd_Property属性的属性值:

HANDLE handle = ::GetProp(hTargetWnd, _T("Target_Paint_Wnd_Property"));

如果为1,则表示该窗口就是我们要找的目标窗口。

6、调用SetWindowLong给目标窗口设置新的窗口处理函数,在新窗口处理函数中拦截消息

       先调用API函数GetWindowLong,传入GWL_WNDPROC参数,获取目标窗口当前的窗口处理函数,并保存下来。然后再调用API函数SetWindowLong,传入GWL_WNDPROC参数,给目标窗口设置新的窗口处理函数,在新的窗口处理函数中拦截消息,示例代码如下:

HWND g_hRecvMsgWnd = NULL;
WNDPROC g_oldWndProc = NULL;// 用于拦截消息的新的窗口处理过程
LRESULT CALLBACK NewWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{// 在此处使用消息号来拦截消息//if ( uMsg == WM_XXXXXXX)//{//}// 不拦截的消息还是交给老的窗口处理过程去处理if (g_oldWndProc != NULL){return CallWindowProc(g_oldWndProc, hWnd, uMsg, wParam, lParam);}return 0;
}void MainProcFunc()
{// 创建消息接收窗口g_hRecvMsgWnd = ::CreateWindowEx(0, _T("Static"), _T("RecvMsgWnd"), WS_DISABLED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, 0, 0);if (g_hRecvMsgWnd != NULL){// 先保存老的窗口处理函数,然后设置新的窗口处理函数NewWndProc,这样就可以// 在NewWndProc中拦截消息了g_oldWndProc = reinterpret_cast<WNDPROC>(GetWindowLong(g_hRecvMsgWnd, GWL_WNDPROC));SetWindowLong(g_hRecvMsgWnd, GWL_WNDPROC, reinterpret_cast<s32>(NewWndProc));}else{g_oldWndProc = NULL;}
}

相关文章:

处理窗口的常用API函数及窗口处理经验总结(附源码)

目录 1、检测窗口状态 2、将窗口前置显示 2.1、将窗口拉到最前面显示 2.2、将窗口置顶显示 2.3、将窗口设置到指定窗口的上面 3、将不显示的窗口强行显示出来 4、获取窗口的信息 5、通过窗口信息去查找窗口 5.1、调用GetClassName接口去比对窗口的类名 5.2、调用Find…...

@TableId注解详细介绍

TableId注解是专门用在主键上的注解&#xff0c;如果数据库中的主键字段名和实体中的属性名&#xff0c;不一样且不是驼峰之类的对应关系&#xff0c;可以在实体中表示主键的属性上加Tableid注解&#xff0c;并指定Tableid注解的value属性值为表中主键的字段名既可以对应上。 …...

kubectl常用的命令

目录 安装 kubectl 一、命令自动补全 二、常用命令 1、查看所有pod列表 2、查看RC和service列表 3、显示Node的详细信息 4、显示Pod的详细信息, 特别是查看Pod无法创建的时候的日志 5、 根据yaml创建资源, apply可以重复执行&#xff0c;create不行 6、基于nginx.yaml…...

Linux 配置远程SSH服务(密码+密钥)

环境准备&#xff1a; 将虚拟机1恢复快照&#xff0c;然后手动配置一个NAT模式IP为192.168.200.100&#xff0c;hostname设置为fuwu1 将虚拟机1复制为虚拟机2&#xff0c;然后手动配置一个NAT模式IP为192.168.200.200&#xff0c;hostname设置为fuwu2 windows准备 xshell 或 pu…...

WuThreat身份安全云-TVD每日漏洞情报-2023-02-20

漏洞名称:Microsoft Exchange Server 远程执行代码漏洞 漏洞级别:高危 漏洞编号:CVE-2023-21529,CNNVD-202302-1075 相关涉及:Microsoft Exchange Server 2016 Cumulative Update 23 漏洞状态:POC 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-03822 漏洞…...

面试经常被问悲观锁和乐观锁?什么是cas?来我花3分钟时间告诉你

锁大家都知道吧&#xff0c;多线程访问资源会存在竞争&#xff0c;那么就需要加锁进而让多个线程一个一个访问。 比如有一个房间&#xff0c;一次只能进一个人&#xff0c;现在有十个人都想进去怎么办&#xff1f; 对&#xff0c;加锁。拿一把钥匙&#xff0c;谁抢到钥匙谁就…...

React源码分析3-render阶段(穿插scheduler和reconciler)

本章将讲解 react 的核心阶段之一 —— render阶段&#xff0c;我们将探究以下部分内容的源码&#xff1a; 更新任务的触发更新任务的创建reconciler 过程同步和异步遍历及执行任务scheduler 是如何实现帧空闲时间调度任务以及中断任务的 触发更新 触发更新的方式主要有以下几…...

3功能测试心得分享

1. 登陆、添加、删除、查询模块是我们经常遇到的&#xff0c;这些模块的测试点该如何考虑 (1)登陆 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求&#xff0c;密码不符合要求(格式上的要求) ④ 密码符合要求&#xff…...

Python-推导式

Python 推导式 Python 推导式是一种独特的数据处理方式&#xff0c;可以从一个数据序列构建另一个新的数据序列的结构体。 Python 支持各种数据结构的推导式&#xff1a; 列表(list)推导式 字典(dict)推导式 集合(set)推导式 元组(tuple)推导式 列表推导式 列表推导式格式…...

操作系统线程

进程那一章&#xff0c;我们留下了一个问题 第一个cpu调用进程&#xff0c;进程调用i/o设备&#xff0c;主动进入ready 队列 第二个cpu将程序执行时间平均分时&#xff0c;进程执行时间到 第三个fork函数&#xff0c;我们上一章的lab有实践&#xff0c;可以看出是父进程主动条用…...

vue3中如何定义响应式变量

vue2中定义方式&#xff1a; 熟悉vue2的前端开发小伙伴&#xff0c;都知道定义变量的方式是属于 选项式写法&#xff0c;所有的变量名全都定义在 data(){return { title:‘hello world’}},里&#xff0c;如下图所示&#xff1a; <template><div><h1>{{tit…...

【C++修炼之路】20.手撕红黑树

每一个不曾起舞的日子都是对生命的辜负 红黑树实现:RBTree 前言一.红黑树的概念及性质1.1 红黑树的概念1.2 红黑树的性质二.红黑树的结构2.1 红黑树节点的定义2.2 红黑树类的封装三.红黑树的插入情况1&#xff1a;只变色情况2&#xff1a;变色单旋情况3&#xff1a;双旋插入的代…...

树状数组(高级数据结构)-蓝桥杯

一、简介树状数组 (Binary Indexed Tree,BIT)&#xff0c;利用数的二进制特征进行检索的一种树状结构。一种真正的高级数据结构&#xff1a; 二分思想、二叉树、位运算、前缀和。高效!代码极其简洁!二、基本应用数列a1,a2,....,an&#xff0c;操作&#xff1a;单点修改&#xf…...

Flink-多流转换(Union、Connect、Join)

文章目录多流转换分流基本合流操作联合&#xff08;Union&#xff09;连接&#xff08;Connect&#xff09;基于时间的合流——双流联结&#xff08;Join&#xff09;窗口联结&#xff08;Window Join&#xff09;间隔联结&#xff08;Interval Join&#xff09;窗口同组联结&a…...

kubeadmin安装k8s集群

目录 一 、环境部署 1、服务器规划 2、环境准备 二、所有节点安装docker 1、配置yum源&#xff0c;安装docker 2、配置daemon.json文件 三、所有节点安装kubeadm、kubelet 和kubectl 四、部署k8s集群 1、查看初始化需要的镜像 2、导入镜像 3、初始化kubeadm 3.1 方…...

java3月train笔记

java笔记 day01 一、jdk和idea下载及安装&#xff08;一般不建议装C盘&#xff09;&#xff1a; jdk&#xff1a;java开发环境 idea&#xff1a;开发工具&#xff08;软件&#xff09;&#xff0c;用来编写代码的 苍老师文档服务器&#xff1a;doc.canglaoshi.org jdk下载&…...

Apollo Config原理浅析

文章目录1. 简介2. 基本功能3. Apollo关键功能实现原理3.1 框架整体原理3.1.1 Apollo角色3.1.2 框架执行原理3.1.3 整体组成3.2 细节实现3.2.1 Eureka和不同角色机器的关系3.2.2 Meta Server的作用3.2.3 ReleaseMessage消息实现原理3.2.4 Client的通信方式1. 简介 apollo是携程…...

Kubernetes二 Kubernetes之实战以及pod详解

Kubernetes入门 一 Kubernetes实战 本章节将介绍如何在kubernetes集群中部署一个nginx服务&#xff0c;并且能够对其进行访问。 1.1 Namespace Namespace是kubernetes系统中的一种非常重要资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。…...

机械革命黑苹果改造计划第四番-外接显示器、win时间不正确问题解决

问题 1.无法外接显示器 最大的问题就是目前无法外接显示器&#xff0c;因为机械革命大多数型号笔记本电脑的HDMI、DP接口都是直接物理接在独显上的&#xff0c;内屏用核显外接显示器接独显&#xff0c;英伟达独显也是黑苹果无法驱动的&#xff0c;而且发现机械革命tpyec接口还…...

Linux docker(03)可使用GPU渲染的x11docker实战总结

该系列文章的目的旨在之前的章节基础上&#xff0c;使用x11docker构建一个可以使用GPU的docker容器。该容器可以用于3D图形渲染/XR 等使用GPU渲染的程序调试和运行。 0 why docker 为什么非要用x11docker&#xff0c;而不是其他的docker呢&#xff1f; 因为一般的docker是不…...

Zellij远程认证终极指南:OAuth、SSH与令牌管理全解析

Zellij远程认证终极指南&#xff1a;OAuth、SSH与令牌管理全解析 【免费下载链接】zellij A terminal workspace with batteries included 项目地址: https://gitcode.com/gh_mirrors/ze/zellij 欢迎来到Zellij终端工作区的远程认证世界&#xff01;&#x1f680; Zelli…...

PyQt5开发环境搭建:Docker容器内GUI调用宿主机显示的完整指南(Ubuntu22.04+20.04)

PyQt5开发环境搭建&#xff1a;Docker容器内GUI调用宿主机显示的完整指南&#xff08;Ubuntu22.0420.04&#xff09; 在跨团队协作开发中&#xff0c;环境一致性往往是影响开发效率的关键因素。当团队成员使用不同版本的Ubuntu系统时&#xff0c;PyQt5这类依赖特定系统库的GUI开…...

三维空间频谱时序预测模型开发完整报告

三维空间频谱时序预测模型开发完整报告 一、项目背景与目标 本项目基于UrbanRadio3D静态数据集,构建端到端的深度学习模型,实现对低空三维空间频谱(路径损耗)的时序演化预测。城市环境中的无线电传播受建筑物遮挡、反射等因素影响,呈现出复杂的空间分布和时间动态特性(…...

商品详情API的SLA保障体系:监控告警、异常检测与自动化修复

在电商业务中&#xff0c;商品详情API是连接前端展示与后端数据的核心枢纽&#xff0c;其稳定性、可用性直接决定用户体验与业务转化——用户点击商品卡片后&#xff0c;若API响应延迟、数据异常或服务中断&#xff0c;会直接导致用户流失、订单损失。SLA&#xff08;服务等级协…...

漏洞挖掘变现指南:合法渠道\+赏金技巧,新手也能月入过万

很多新手学习漏洞挖掘&#xff0c;不仅是为了提升技术&#xff0c;更是为了实现“技术变现”——通过提交漏洞获取赏金、兼职接单&#xff0c;这也是网络安全领域的核心福利之一。但变现的前提是“合法合规”&#xff0c;本文拆解漏洞挖掘的合法变现渠道、赏金提升技巧、避坑指…...

3步实现跨平台文献管理效率跃升:WPS-Zotero开源工具深度应用指南

3步实现跨平台文献管理效率跃升&#xff1a;WPS-Zotero开源工具深度应用指南 【免费下载链接】WPS-Zotero An add-on for WPS Writer to integrate with Zotero. 项目地址: https://gitcode.com/gh_mirrors/wp/WPS-Zotero 在学术研究的数字化工作流中&#xff0c;如何解…...

BaGet实战教程:如何配置和使用镜像功能加速包下载

BaGet实战教程&#xff1a;如何配置和使用镜像功能加速包下载 【免费下载链接】BaGet A lightweight NuGet and symbol server 项目地址: https://gitcode.com/gh_mirrors/ba/BaGet BaGet是一款轻量级的NuGet和符号服务器&#xff0c;通过配置其镜像功能&#xff0c;开发…...

musescore-downloader多语言支持解析:国际化i18n实现原理

musescore-downloader多语言支持解析&#xff1a;国际化i18n实现原理 【免费下载链接】musescore-downloader ⚠️ This repo has moved to https://github.com/LibreScore/dl-librescore ⚠️ | Download sheet music (MSCZ, PDF, MusicXML, MIDI, MP3, download individual p…...

如何基于SecGPT构建企业级安全智能助手:完整实践指南

如何基于SecGPT构建企业级安全智能助手&#xff1a;完整实践指南 【免费下载链接】SecGPT SecGPT网络安全大模型 项目地址: https://gitcode.com/gh_mirrors/se/SecGPT SecGPT作为一款专业的网络安全大模型&#xff0c;为企业构建智能化安全防护体系提供了强大支持。本文…...

如何用本地备份打造数字记忆保险箱?GetQzonehistory全攻略

如何用本地备份打造数字记忆保险箱&#xff1f;GetQzonehistory全攻略 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 在这个信息爆炸的时代&#xff0c;我们的数字足迹如同沙滩上的脚印…...