pywin32 win32gui.EnumWindows() 如何去除无意义的窗口句柄

2020-04-23 01:18:16 +08:00
 Leon6868

现在的代码

hideClass = ["IME","Syn Zoom Class","MyWing","MSCTFIME UI"]

def reset_window_pos(targetTitle):  
    hWndList = []  
    win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), hWndList)  
    count = 0
    for hwnd in hWndList:
        if win32gui.GetWindowText(hwnd) != "":
            if win32gui.GetClassName(hwnd) not in hideClass:
                print("count:",count)
                print(hwnd)
                print(win32gui.GetWindowText(hwnd))
                print(win32gui.GetClassName(hwnd))
                print("-"*10)
                count += 1

这个代码运行后,还剩下 80 个句柄(我只打开了 5 个窗口)
只能通过枚举类名的方式排除句柄吗?还是有其他的 api 可以做到?

4179 次点击
所在节点    Python
10 条回复
geelaw
2020-04-23 01:26:44 +08:00
定义“无意义”,如果当前没有显示的窗口是无意义,你可以通过 IsWindowVisible 和 DwmGetWindowAttribute 判断。
mingl0280
2020-04-23 04:33:24 +08:00
正常过滤窗口 handle 都是白名单形式的,因为你根本不知道其它窗口会不会跟你的有同样的 class 和 title
opengps
2020-04-23 07:40:33 +08:00
我当年写的 socket 服务端,句柄泄露到上百万个都还能正常运行,你才 80 个担心啥
geelaw
2020-04-23 09:45:34 +08:00
@opengps #3 这个问题和句柄泄露无关,USER 句柄是不计数的,泄露方式只能是建立的窗口之后不使用。而且一个会话里的 USER 句柄数量最多是 65536 。

“用户不能操作”仍然不是一个有意义的定义,而且用户当然可以操作 IME 窗口,不然候选词列表怎么用?如果你觉得任务栏是否显示这个窗口的按钮可以作为判据,那么你可以模拟任务栏的选择,这是有文档记录的:

https://docs.microsoft.com/en-us/windows/win32/shell/taskbar#managing-taskbar-buttons
Leon6868
2020-04-23 12:38:22 +08:00
@geelaw
谢谢,我的“无意义”指的是“桌面上可以被用户操作的窗口”,前面表述的很不清楚,感谢指正:D
我现在用 IsWindowVisible 判断,可是在 win10 还是有一些问题
这是我解析出来的窗口

['QQ',
'腾讯会议',
'D:\\Temp\\win32.py - Sublime Text (UNREGISTERED)',
'腾讯会议',
'群等 5 个会话',
'电影和电视',
'电影和电视',
'设置',
'设置',
'邮件',
'收件箱 - Outlook \u200e- 邮件',
'Microsoft Store',
'Microsoft Store',
'Title',
'便笺',
'Microsoft Text Input Application',
'便笺',
'Program Manager']

但是我并没有打开“电影和电视”以及“设置”、“'Microsoft Store”和“收件箱 - Outlook \u200e- 邮件”,你知道为什么吗?
mingl0280
2020-04-23 15:00:34 +08:00
@Leon6868 很简单,你看起来桌面上只有那么多窗口,但是实际上 windows 里面的窗口不止这么多.
你要是想遍历所有打开在桌面上的顶层窗口的话,
python 的库我没用过,不过 C 的话需要做这个过滤:
使用 GetWindowTextA 获取窗口标题,如果为空跳过.
使用 uint dwStyle = GetWindowLong(hWnd, GWL_STYLE (-16))获取窗口的样式表,然后判断 dwStyle & WS_VISIBLE (0x10000000) == 1 为可见窗口.

C/C++的代码基本上长这样
BOOL CALLBACK EnumProc(HWND hWnd, LPARAM lParam)
{
LPSTR TitleStr = new char[4096];
LPSTR ClassStr = new char[4096];
GetClassNameA(hWnd, ClassStr, 4096);
GetWindowTextA(hWnd, TitleStr, 4096);
string Title(TitleStr);
string ClassName(ClassStr);
DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
if (dwStyle & WS_VISIBLE && !Title.empty())
{
// 你要的窗口
}
return true;
}
geelaw
2020-04-23 16:28:00 +08:00
@Leon6868 #5 因为 Windows 10 会预热启动 UWP 来提升体验。我说过了你需要用 DwmGetWindowAttribute,这里你需要判断窗口是否被掩盖( cloaked ),一个例子代码见 https://devblogs.microsoft.com/oldnewthing/20200302-00/?p=103507

此外我也提示过你可能想要模拟任务栏或者 Alt+Tab 对话框选择窗口的方式。
Leon6868
2020-04-23 16:41:35 +08:00
@mingl0280 还是获取不到 /(ㄒoㄒ)/~~
现在的代码:

import win32api
import win32con
import win32gui
from pprint import pprint

def reset_window_pos():
hWndList = []
win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), hWndList)
count = 0
for hWnd in hWndList:
if win32gui.GetWindowText(hWnd) and win32gui.IsWindowVisible(hWnd):
dwStyle = win32gui.GetWindowLong(hWnd, win32con.GWL_STYLE)
if dwStyle & win32con.WS_VISIBLE:
print(count,"-"*10)
print("GetWindowText:",win32gui.GetWindowText(hWnd))
print("win32con.WS_VISIBLE:",win32con.WS_VISIBLE)
print("GWL_STYLE:",win32gui.GetWindowLong(hWnd, win32con.GWL_STYLE))
print("GetWindowRect:",win32gui.GetWindowRect(hWnd))
count += 1

部分运行结果:
0 ----------
GetWindowText: QQ
win32con.WS_VISIBLE: 268435456
GWL_STYLE: -1777598464
GetWindowRect: (35, -535, 325, 5)
1 ----------
GetWindowText: F:\ME\beable\LockerTODO\try\win32try\byPywin32.py - Sublime Text (UNREGISTERED)
win32con.WS_VISIBLE: 268435456
GWL_STYLE: 365887488
GetWindowRect: (-7, -7, 1288, 728)
………………………………………………………………………………………………
5 ----------
GetWindowText: 设置
win32con.WS_VISIBLE: 268435456
GWL_STYLE: -1811939328
GetWindowRect: (0, 1, 719, 486)
6 ----------
GetWindowText: 设置
win32con.WS_VISIBLE: 268435456
GWL_STYLE: -1798373376
GetWindowRect: (481, 80, 1214, 573)
7 ----------
GetWindowText: 邮件
win32con.WS_VISIBLE: 268435456
GWL_STYLE: -1811939328
GetWindowRect: (0, 1, 719, 656)
8 ----------
GetWindowText: 收件箱 - Outlook ‎- 邮件
win32con.WS_VISIBLE: 268435456
GWL_STYLE: -1798373376
GetWindowRect: (0, 57, 733, 720)
9 ----------
GetWindowText: 电影和电视
win32con.WS_VISIBLE: 268435456
GWL_STYLE: -1811939328
GetWindowRect: (0, 1, 711, 426)
10 ----------
GetWindowText: 电影和电视
win32con.WS_VISIBLE: 268435456
GWL_STYLE: -1798373376
GetWindowRect: (108, 201, 834, 634)
Leon6868
2020-04-23 17:20:37 +08:00
@geelaw 谢谢!用 DwmGetWindowAttribute 搞定了
果然还是原生的 ctype 靠谱
geelaw
2020-04-24 17:49:29 +08:00
Cloaking 的概念和 UWP 没关系,如果你用虚拟桌面的话,不在当前桌面显示的窗口都会被 cloaked 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/665175

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX