引言
昨日lexli在工作中遇到这样一个问题:一款性能测试软件需要知道桌面上窗口的Title,同时需要知道软件启动后Title分别被设置了几次,设置的title分别对应什么。通常我们可以直接查看窗口的缩略图,得到对应的标题。但是,对于软件启动后设置了多少次title以及每次title分别被设置为什么,这个问题突然感到无从下手。不过幸好公司有熟悉windows编程的高手,稍微一点拨知道原来可以通过windows API获得这些相关信息。下面,将自己经过点拨后的思路记录下来,方便以后不定期回顾。
需求分析
这里,通过对原来问题进行分解,可以将这个问题抽象为这样两个步骤:
* 遍历当前桌面上的所有窗口,得到每一个窗口的HWND
* 根据遍历到的窗口HWND,获取窗口的Title以及ClassName
而这两个步骤正好对应了相关的windows API.他们分别是:
EnumWindows
EnumChildWindows
GetWindowText
GetClassName
这里分别介绍下上面四个API:EnumWindows可以遍历当前屏幕上所有的父窗口(带有WS_CHILD属性风格);EnumChildWindows配合EnumWindows,可以遍历父窗口下所有的子窗口(以及子窗口的子窗口,会进行递归查找);GetWindowText可以通过遍历到的HWND得到对应window的Title;GetClassName则通过遍历得到的HWND获取对应Window的Class属性。了解了上述API属性后,我们就可以编写相关代码来进行验证了。
代码验证
按照上文提出的需求分解,我们将首先实现第一个步骤:得到每一个窗口的HWND。
#include <iostream>
#include <Windows.h>
using namespace std;
BOOL CALLBACK EnumChildProc(_In_ HWND hwnd, _In_ LPARAM lParam)
{
cout << "[Child window] window handle: " << hwnd << endl;
return TRUE;
}
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
/*
* Remarks
The EnumWindows function does not enumerate child windows,
with the exception of a few top-level windows owned by the
system that have the WS_CHILD style.
*/
cout << "[Parent window] window handle: " << hwnd << endl;
EnumChildWindows(hwnd, EnumChildProc, lParam);
return TRUE;
}
int main(int argc, char *argv[])
{
EnumWindows(EnumWindowsProc, 0);
return 0;
}
注意 回调函数的返回值必须为TRUE才能保证系统会依次遍历每一个窗口。如果返回值非TRUE,则在当前窗口后不会进行后续的遍历动作。
在得到了所有窗口的HWND后,获取Title和ClassName就太简单了,直接用窗口的HWND + API去获取。这样,整个需求的完整实现如下:
#include <iostream>
#include <Windows.h>
using namespace std;
BOOL CALLBACK EnumChildProc(_In_ HWND hwnd, _In_ LPARAM lParam)
{
char szTitle[MAX_PATH] = {0};
char szClass[MAX_PATH] = {0};
int nMaxCount = MAX_PATH;
LPSTR lpClassName = szClass;
LPSTR lpWindowName = szTitle;
GetWindowTextA(hwnd, lpWindowName, nMaxCount);
GetClassNameA(hwnd, lpClassName, nMaxCount);
cout << "[Child window] window handle: " << hwnd << " window name: "
<< lpWindowName << " class name " << lpClassName << endl;
return TRUE;
}
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
/*
* Remarks
The EnumWindows function does not enumerate child windows,
with the exception of a few top-level windows owned by the
system that have the WS_CHILD style.
*/
char szTitle[MAX_PATH] = {0};
char szClass[MAX_PATH] = {0};
int nMaxCount = MAX_PATH;
LPSTR lpClassName = szClass;
LPSTR lpWindowName = szTitle;
GetWindowTextA(hwnd, lpWindowName, nMaxCount);
GetClassNameA(hwnd, lpClassName, nMaxCount);
cout << "[Parent window] window handle: " << hwnd << " window name: "
<< lpWindowName << " class name " << lpClassName << endl;
EnumChildWindows(hwnd, EnumChildProc, lParam);
return TRUE;
}
int main(int argc, char *argv[])
{
EnumWindows(EnumWindowsProc, 0);
return 0;
}
结论
- EnumWindows API是实现该需求的核心,需要了解其含义
- GetWindowText以及GetClassName都是可以查MSDN得到了
- 最重要的一点:对于自己不熟悉的领域,多交流,别人的一句点拨可以是你少走太多的弯路。