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 可以做到?
感谢@geelaw 解决方法是:检测窗体是否隐形
因为UWP 具有一个称为“ 隐身”的概念,在该概念中,窗口被赋予了所有可见性陷阱,而实际上并未呈现给用户。就该应用程序而言,它似乎是可见的:它仍然具有WS_VISIBLE窗口样式,其坐标仍在监视器的范围内,它仍然获取消息,它具有一个非空的裁剪区域,所以用WS_VISIBLE和GetWindowRect检测会失效。
具体原因可以查看这里
直接调用dwmapi.dll
的DwmGetWindowAttribute
方法就可以检测是否隐形了。
Python原生实现:
import ctypes
import ctypes.wintypes
from pprint import pprint
def get_current_size(hWnd):
try:
f = ctypes.windll.dwmapi.DwmGetWindowAttribute
except WindowsError:
f = None
if f:
DWMWA_CLOAKED = 14
cloaked = ctypes.wintypes.DWORD()
f(hWnd, #在这里检测
ctypes.wintypes.DWORD(DWMWA_CLOAKED),
ctypes.byref(cloaked),
ctypes.sizeof(cloaked)
)
return cloaked.value
def getTitleList():
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
titles = []
dwmapi = ctypes.windll.dwmapi
def foreach_window(hwnd, lParam):
if IsWindowVisible(hwnd) and GetWindowTextLength(hwnd) != 0 and not get_current_size(hwnd):
length = GetWindowTextLength(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
GetWindowText(hwnd, buff, length + 1)
titles.append(buff.value)
return True
EnumWindows(EnumWindowsProc(foreach_window), 0) #这里其实可以用匿名函数
return titles
pprint(getTitleList())
1
geelaw 2020-04-23 01:26:44 +08:00 via iPhone 2
定义“无意义”,如果当前没有显示的窗口是无意义,你可以通过 IsWindowVisible 和 DwmGetWindowAttribute 判断。
|
2
mingl0280 2020-04-23 04:33:24 +08:00 via Android
正常过滤窗口 handle 都是白名单形式的,因为你根本不知道其它窗口会不会跟你的有同样的 class 和 title
|
3
opengps 2020-04-23 07:40:33 +08:00 via Android
我当年写的 socket 服务端,句柄泄露到上百万个都还能正常运行,你才 80 个担心啥
|
4
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 |
5
Leon6868 OP @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- 邮件”,你知道为什么吗? |
6
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; } |
7
geelaw 2020-04-23 16:28:00 +08:00 1
@Leon6868 #5 因为 Windows 10 会预热启动 UWP 来提升体验。我说过了你需要用 DwmGetWindowAttribute,这里你需要判断窗口是否被掩盖( cloaked ),一个例子代码见 https://devblogs.microsoft.com/oldnewthing/20200302-00/?p=103507
此外我也提示过你可能想要模拟任务栏或者 Alt+Tab 对话框选择窗口的方式。 |
8
Leon6868 OP @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) |
10
geelaw 2020-04-24 17:49:29 +08:00
Cloaking 的概念和 UWP 没关系,如果你用虚拟桌面的话,不在当前桌面显示的窗口都会被 cloaked 。
|