From 51b54b010f5c88932324a573329c762d730d8c10 Mon Sep 17 00:00:00 2001 From: LZY <2217445143@qq.com> Date: Sat, 7 Mar 2026 17:45:05 +0800 Subject: [PATCH] =?UTF-8?q?add:=E7=AA=97=E5=8F=A3=E5=8C=BA=E5=9F=9F?= =?UTF-8?q?=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../all_windows_hwnd.py" | 29 ------- .../get_all_hwnd.py" | 85 +++++++++++++++++++ .../main.py" | 34 ++++++++ .../move_all.py" | 57 +++++++++++++ .../taskbar_scroll.py" | 36 ++++++++ 5 files changed, 212 insertions(+), 29 deletions(-) delete mode 100644 "\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/all_windows_hwnd.py" create mode 100644 "\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/get_all_hwnd.py" create mode 100644 "\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/move_all.py" create mode 100644 "\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/taskbar_scroll.py" diff --git "a/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/all_windows_hwnd.py" "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/all_windows_hwnd.py" deleted file mode 100644 index e759e66..0000000 --- "a/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/all_windows_hwnd.py" +++ /dev/null @@ -1,29 +0,0 @@ -import comtypes.client -import win32gui -import win32process -import win32api -import psutil - -def get_taskbar_app_process(): - uia = comtypes.client.CreateObject("UIAutomationClient.CUIAutomation") - x, y = win32api.GetCursorPos() - - element = uia.ElementFromPoint((x, y)) - if not element: - return None - - name = element.CurrentName - if not name: - return None - - # 枚举所有顶级窗口,匹配标题 - result = [] - def enum(hwnd, _): - title = win32gui.GetWindowText(hwnd) - if name in title: - result.append(hwnd) - win32gui.EnumWindows(enum, None) - - return result - -print(get_taskbar_app_process()) \ No newline at end of file diff --git "a/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/get_all_hwnd.py" "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/get_all_hwnd.py" new file mode 100644 index 0000000..00da30a --- /dev/null +++ "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/get_all_hwnd.py" @@ -0,0 +1,85 @@ +import win32gui +import win32con +import ctypes + +# 加载系统 shell32 库 +shell32 = ctypes.windll.shell32 +ole32 = ctypes.windll.ole32 + +# 定义 IVirtualDesktopManager 的 GUID +CLSID_VirtualDesktopManager = ctypes.c_char_p( + b'\xaa\x12\x1d\x01\xc8\xc3\x2e\x42\x87\xdd\x2f\xaf\xfd\x5a\x7f\x2f') + + +def is_on_current_desktop(hwnd): + """判断窗口是否在当前虚拟桌面(使用底层 COM 接口)""" + try: + # 获取接口实例 + class VirtualDesktopManager(ctypes.Structure): + pass + pVDM = ctypes.POINTER(VirtualDesktopManager)() + + # 初始化 COM + ole32.CoInitialize(None) + + # 创建 IVirtualDesktopManager 实例 + # CLSID: {aa121d01-c8c3-422e-87dd-2faffd5a7f2f} + clsid = (ctypes.c_ubyte * 16)(* + b'\x01\x1d\x12\xaa\xc3\xc8\x2e\x42\x87\xdd\x2f\xaf\xfd\x5a\x7f\x2f') + iid = (ctypes.c_ubyte * 16)(* + b'\x20\x2b\x16\xa5\xaf\x11\xcd\x4c\x83\xe8\x6e\x37\x45\x28\x73\xcb') + + # 获取接口指针并查询 + # 注意:这里直接简化逻辑,如果无法判断则默认返回 True 以防漏掉窗口 + # 但通常对于普通应用窗口,这个逻辑是有效的。 + return True # 如果底层调用复杂,我们换一种更通用的任务栏过滤方法 + except: + return True + + +def is_real_visible_window(hwnd): + # 1. 基础可见性 + if not win32gui.IsWindowVisible(hwnd): + return False + + # 2. 核心过滤:任务栏逻辑 (这是区分不同桌面最快的方法) + # 虚拟桌面上的隐藏窗口通常会有特定的样式或最小化状态 + # 关键点:检查窗口是否被“挂起” (Cloaked) + # 如果窗口在其他桌面,它会被系统 Cloak(掩盖) + DWMWA_CLOAKED = 14 + cloaked = ctypes.c_int(0) + ctypes.windll.dwmapi.DwmGetWindowAttribute( + hwnd, DWMWA_CLOAKED, ctypes.byref(cloaked), ctypes.sizeof(cloaked) + ) + if cloaked.value != 0: # 如果被掩盖了(在其他桌面或隐藏),则过滤掉 + return False + + # 3. 标题与类名过滤 + title = win32gui.GetWindowText(hwnd) + class_name = win32gui.GetClassName(hwnd) + if not title or class_name in ["Windows.UI.Core.CoreWindow", "Shell_TrayWnd", "Progman"]: + return False + + # 4. 过滤最小化 + if win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMINIMIZED: + return False + + # 5. 样式过滤 + ex_style = win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) + if ex_style & win32con.WS_EX_TOOLWINDOW: + return False + owner = win32gui.GetWindow(hwnd, win32con.GW_OWNER) + + return owner == 0 or (ex_style & win32con.WS_EX_APPWINDOW) + + +def get_visible_hwnds(): + hwnds = [] + win32gui.EnumWindows(lambda h, _: hwnds.append( + h) if is_real_visible_window(h) else None, None) + return hwnds + + +if __name__ == "__main__": + for hwnd in get_visible_hwnds(): + print(f"窗口句柄: {hwnd}, 标题: {win32gui.GetWindowText(hwnd)}") diff --git "a/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/main.py" "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/main.py" index e69de29..9f1bc05 100644 --- "a/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/main.py" +++ "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/main.py" @@ -0,0 +1,34 @@ +import win32gui +from taskbar_scroll import listen_taskbar_scroll +import move_all + +# 设定允许窗口活动的 X 坐标范围 +# 如果你想让窗口能彻底滑出屏幕右侧,MAX_X 设为 3000 +# 如果想让窗口能彻底滑出屏幕左侧,MIN_X 设为 -3000 +MAX_X = 3000 +MIN_X = -3000 + +step = 100 # 300 / 3 + +for scroll in listen_taskbar_scroll(): + # 获取第一个窗口作为坐标基准 + hwnds = move_all.get_visible_hwnds() + if not hwnds: continue + + # 获取当前 X 坐标 + curr_x = win32gui.GetWindowRect(hwnds[0])[0] + + # 逻辑判断: + # 1. 向上滚 (scroll > 0) 且 还没超过右边界 -> 允许右移 + if scroll > 0: + if curr_x < MAX_X: + for _ in range(3): move_all.move(step) + else: + print("已到达右侧极限") + + # 2. 向下滚 (scroll < 0) 且 还没超过左边界 -> 允许左移 + elif scroll < 0: + if curr_x > MIN_X: + for _ in range(3): move_all.move(-step) + else: + print("已到达左侧极限") diff --git "a/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/move_all.py" "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/move_all.py" new file mode 100644 index 0000000..2cca2f4 --- /dev/null +++ "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/move_all.py" @@ -0,0 +1,57 @@ +from get_all_hwnd import get_visible_hwnds +import win32gui +import win32con + +def move(offset_x): + # 1. 获取全量句柄 + hwnds = get_visible_hwnds() + + for hwnd in hwnds: + try: + # 2. 获取当前位置 (left, top, right, bottom) + rect = win32gui.GetWindowRect(hwnd) + + # 3. 计算新位置:当前 X + 偏移量 + new_x = rect[0] + offset_x + new_y = rect[1] # Y 轴保持不变 + + # 4. 设置位置 (SWP_NOSIZE 保持大小, SWP_NOACTIVATE 不抢焦点) + win32gui.SetWindowPos( + hwnd, 0, new_x, new_y, 0, 0, + win32con.SWP_NOSIZE | win32con.SWP_NOZORDER | win32con.SWP_NOACTIVATE + ) + except: + # 某些系统核心窗口拒绝访问时直接跳过 + continue + +# 重置窗口位置的函数(可选) +def reset(): + hwnds = get_visible_hwnds() + for hwnd in hwnds: + try: + # 获取当前位置 + rect = win32gui.GetWindowRect(hwnd) + # 计算新位置:重置到初始位置(假设初始位置是屏幕左侧) + new_x = 0 + new_y = rect[1] # Y 轴保持不变 + + # 设置位置 + win32gui.SetWindowPos( + hwnd, 0, new_x, new_y, 0, 0, + win32con.SWP_NOSIZE | win32con.SWP_NOZORDER | win32con.SWP_NOACTIVATE + ) + except: + continue + +if __name__ == "__main__": + # 向右移动 3000px 输入 3000 + # 向左移动 3000px 输入 -3000 + import time + for i in range(100): + move(10) # 每次移动 30px + time.sleep(0.01) # 每次移动后等待 10ms + for i in range(100): + move(-10) # 每次移动 30px + time.sleep(0.01) # 每次移动后等待 10ms + reset() + print("移动操作已完成") diff --git "a/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/taskbar_scroll.py" "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/taskbar_scroll.py" new file mode 100644 index 0000000..f6bbd0f --- /dev/null +++ "b/\347\252\227\345\217\243\345\214\272\345\237\237\346\211\251\345\261\225/taskbar_scroll.py" @@ -0,0 +1,36 @@ +import win32gui +from pynput import mouse +from queue import Queue + +TASKBAR_CLASSES = {"Shell_TrayWnd", "SecondaryTrayWnd"} + +def is_cursor_on_taskbar(): + try: + _, _, (x, y) = win32gui.GetCursorInfo() + hwnd = win32gui.WindowFromPoint((x, y)) + while hwnd: + if win32gui.GetClassName(hwnd) in TASKBAR_CLASSES: + return True + hwnd = win32gui.GetParent(hwnd) + except: + pass + return False + +def listen_taskbar_scroll(): + """使用生成器封装,支持 for 循环""" + event_queue = Queue() + + def _on_scroll(x, y, dx, dy): + if is_cursor_on_taskbar(): + event_queue.put(dy) + + # 在后台启动监听 + listener = mouse.Listener(on_scroll=_on_scroll) + listener.start() + + try: + while True: + # 从队列中获取事件,这会让 for 循环在没有滚动时“阻塞”等待,不占 CPU + yield event_queue.get() + finally: + listener.stop() -- Gitee