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

news2024/11/15 9:42:52

目录

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;
    }
}

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

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

相关文章

清理bib文件(删除重复项,仅保留tex中引用的条目)

在写latex文件的过程中&#xff0c;经常会遇到添加了一堆文献的bibtex到bib文件中&#xff0c;有时候文章一长同一篇文献用不同的cite-key引用了多次&#xff0c;同时也会有一些文献最后并没被正文引用&#xff0c;这就需要对bib文件进行清理。 删除重复项 可以用JabRef 在J…

45岁当打之年再创业,剑指中国版ChatGPT,这位美团联合创始人能否圆梦?

文 BFT机器人 “即便只有一个人&#xff0c;我也要出发。” 这是45岁的前美团联合创始人王慧文再次冲上创业沙场的“征战”宣言&#xff0c;这一次他的梦想是“组队拥抱新时代&#xff0c;打造中国OpenAI”。 01 当打之年&#xff0c; AI新梦再起航 “我的人工智能宣言&…

视频投票和图文投票之间的差异投票链接制作平台微擎投票

“我的舞台我的梦”网络评选投票_线上小程序的投票方式_视频投票的功能_在线投票程序用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服…

Spring中的拦截器

这里写目录标题基本概念HandlerInterceptor拦截器HandlerInterceptor讲解MethodInterceptor拦截器二者的区别基本概念 在web开发中&#xff0c;拦截器是经常用到的功能。它可以帮我们预先设置数据以及统计方法的执行效率等等。 Spring中拦截器主要分两种&#xff0c;一个是Han…

【学习总结】激光雷达与相机外参标定:代码(cam_lidar_calibration)

前段时间尝试了一款激光雷达和相机标定的代码&#xff0c;总结了博客&#xff1a; 【学习总结】激光雷达与相机外参标定&#xff1a;原理与代码 但总觉得那个代码太差劲&#xff0c;而且精度不行&#xff0c;于是又找了些新的代码&#xff0c;体验比之前的好很多&#xff0c;在…

【自然语言处理】主题建模:Top2Vec(理论篇)

主题建模&#xff1a;Top2Vec&#xff08;理论篇&#xff09;Top2Vec 是一种用于 主题建模 和 语义搜索 的算法。它自动检测文本中出现的主题&#xff0c;并生成联合嵌入的主题、文档和词向量。 算法基于的假设&#xff1a;许多语义相似的文档都可以由一个潜在的主题表示。首先…

90后,转行软件测试3年,从月入7000+到月入过万,整理出的这一万字经验分享。

周一发工资了&#xff0c;到手12857.65&#xff0c;美滋滋 今年是我毕业参加工作的第3年&#xff0c;工资终于来到5位数了。上一家公司月薪7000&#xff0c;实际拿到手就6450左右&#xff0c;感觉今年真的是元气满满啊&#xff0c;工资翻倍&#xff0c;良好的人生开端。 想起…

Odoo丨Odoo框架源码研读二:ORM框架与日志

Odoo丨Odoo框架源码研读二&#xff1a;ORM框架与日志 而Odoo在实际开发的大多数场景都是基于它的ORM框架进行的&#xff0c;所以本期我们将带来Odoo框架源码的第二期内容——ORM和日志。 *ORM* Odoo是通过Controller控制器&#xff0c;来控制前后台的交互。上一期我们详细的…

算法专题训练营

动归算法专题 1.拆分词句 是不是,在不在都是可以用动归解决的 状态转义方程不一定都是等式,也有可能是条件 2.三角形 动归算法也不是一定要借助新开空间,也是可以用自己原来的空间 3.背包问题 4.分割回文串-ii 5.不同的子序列 贪心算法专题 只管一步的最优结果, 1.分割平衡…

前缀和差分(C/C++)

目录 1. 前缀和的定义 2. 一维前缀和 2.1 计算公式 2.2 用途 2.3 小试牛刀 3. 二维前缀和 3.1 用途 1. 前缀和的定义 对于一个给定的数列A&#xff0c;他的前缀和数中 S 中 S[ i ] 表示从第一个元素到第 i 个元素的总和。 如下图&#xff1a;绿色区域的和就是前缀和数组…

如何在SpringBoot项目上让接口返回数据脱敏,一个注解即可

1 背景需求是某些接口返回的信息&#xff0c;涉及到敏感数据的必须进行脱敏操作2 思路①要做成可配置多策略的脱敏操作&#xff0c;要不然一个个接口进行脱敏操作&#xff0c;重复的工作量太多&#xff0c;很显然违背了“多写一行算我输”的程序员规范。思来想去&#xff0c;定…

关于数字化营销技术实现之【数据埋点】

1.如何实现数据埋点&#xff1f;小程序数据埋点是指在小程序中收集用户行为数据和业务数据的一种技术手段&#xff0c;以便对用户行为和业务运营进行分析和优化。下面是一些实现小程序数据埋点的方法&#xff1a;使用小程序统计分析工具&#xff1a;小程序平台提供了统计分析工…

约束优化:低维线性时间线性规划算法(Seidel算法)、低维线性时间严格凸二次规划算法

文章目录约束优化&#xff1a;低维线性时间线性规划算法&#xff08;Seidel算法&#xff09;、低维线性时间严格凸二次规划算法带约束优化问题的定义带约束优化问题的分类及时间复杂度低维线性规划问题定义Seidel线性规划算法低维严格凸二次规划问题定义低维情况下的精确最小范…

【LeetCode】剑指 Offer 09. 用两个栈实现队列 p68 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/ 1. 题目介绍&#xff08;09. 用两个栈实现队列&#xff09; 用两个栈实现一个队列。队列的声明如下&#xff0c;请实现它的两个函数 appendTail 和 deleteHead &#xff0c;分别…

【大厂高频必刷真题100题】《是子序列吗?》 真题练习第28题 持续更新~

是子序列吗? 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。 进阶: 如果有…

火山引擎 DataTester:在广告投放场景下的 A/B 实验实践

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 “我知道在广告上的投资有一半是无用的&#xff0c;但问题是我不知道是哪一半。” ——零售大亨约翰沃纳梅克 这句经典名言&#xff0c;被称为广告界的哥特巴赫猜想…

Python脚本批量下载CDS气象数据

使用Python脚本从 Copernicus Climate Data Store (CDS) 检索气象数据具体地&#xff0c;需要检索变量&#xff08;geopotential、relative_humidity、temperature、u_component_of_wind、v_component_of_wind、vertical_velocity&#xff09;在各种不同的压力水平、不同的日期…

罗克韦尔AB PLC_FactoryTalk无法登录的解决方法

罗克韦尔AB PLC_FactoryTalk无法登录的解决方法 情况说明: 在打开Studio 5000软件时,出现一个弹窗Log On to FactoryTalk - Network,正常情况下输入Windows账户和密码就可以登录成功。 但是却出现了下图所示窗口,其中‘abseme’是Windows账户名,‘WELL’是计算机名称,下图…

SQL零基础入门学习(二)

SQL SELECT 语句 SELECT 语句用于从数据库中选取数据。 结果被存储在一个结果表中&#xff0c;称为结果集。 SQL SELECT 语法 SELECT column1, column2, ... FROM table_name;与 SELECT * FROM table_name;参数说明&#xff1a; column1, column2, …&#xff1a;要选择的…

向上跳空缺口选股公式,选出回补后再启动的标的

一、向上跳空缺口选股公式 思路&#xff1a;先找出缺口&#xff0c;缺口前后有两根K线&#xff0c;缺口低价是前一根K线的最高价&#xff0c;缺口高价是后一根K线的最低价。&#xff08;如上图&#xff09;收盘价低于缺口低价&#xff0c;即实现缺口回补。回补缺口之后&#xf…