|
| 1 | +import ctypes |
| 2 | +from ctypes import wintypes |
| 3 | + |
| 4 | +user32 = ctypes.windll.user32 |
| 5 | +kernel32 = ctypes.windll.kernel32 |
| 6 | + |
| 7 | +# 使进程感知 DPI,避免 GetClientRect 返回缩放后的虚拟坐标 |
| 8 | +# 150% 缩放时未设置此项会导致返回值只有实际分辨率的 2/3 |
| 9 | +user32.SetProcessDPIAware() |
| 10 | + |
| 11 | +WNDENUMPROC = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM) |
| 12 | + |
| 13 | +TH32CS_SNAPPROCESS = 0x00000002 |
| 14 | + |
| 15 | + |
| 16 | +class PROCESSENTRY32W(ctypes.Structure): |
| 17 | + _fields_ = [ |
| 18 | + ("dwSize", wintypes.DWORD), |
| 19 | + ("cntUsage", wintypes.DWORD), |
| 20 | + ("th32ProcessID", wintypes.DWORD), |
| 21 | + ("th32DefaultHeapID", ctypes.POINTER(wintypes.ULONG)), |
| 22 | + ("th32ModuleID", wintypes.DWORD), |
| 23 | + ("cntThreads", wintypes.DWORD), |
| 24 | + ("th32ParentProcessID", wintypes.DWORD), |
| 25 | + ("pcPriClassBase", wintypes.LONG), |
| 26 | + ("dwFlags", wintypes.DWORD), |
| 27 | + ("szExeFile", ctypes.c_wchar * 260), |
| 28 | + ] |
| 29 | + |
| 30 | + |
| 31 | +def get_pids_by_name(process_name): |
| 32 | + snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) |
| 33 | + if snapshot == -1: |
| 34 | + return [] |
| 35 | + entry = PROCESSENTRY32W() |
| 36 | + entry.dwSize = ctypes.sizeof(PROCESSENTRY32W) |
| 37 | + pids = [] |
| 38 | + if kernel32.Process32FirstW(snapshot, ctypes.byref(entry)): |
| 39 | + while True: |
| 40 | + if entry.szExeFile.lower() == process_name.lower(): |
| 41 | + pids.append(entry.th32ProcessID) |
| 42 | + if not kernel32.Process32NextW(snapshot, ctypes.byref(entry)): |
| 43 | + break |
| 44 | + kernel32.CloseHandle(snapshot) |
| 45 | + return pids |
| 46 | + |
| 47 | + |
| 48 | +def find_window_by_process(process_name): |
| 49 | + pids = get_pids_by_name(process_name) |
| 50 | + if not pids: |
| 51 | + return None |
| 52 | + pid_set = set(pids) |
| 53 | + results = [] |
| 54 | + |
| 55 | + def callback(hwnd, _lparam): |
| 56 | + if not user32.IsWindowVisible(hwnd): |
| 57 | + return True |
| 58 | + pid = wintypes.DWORD() |
| 59 | + user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid)) |
| 60 | + if pid.value in pid_set: |
| 61 | + results.append(hwnd) |
| 62 | + return True |
| 63 | + |
| 64 | + user32.EnumWindows(WNDENUMPROC(callback), 0) |
| 65 | + return results[0] if results else None |
| 66 | + |
| 67 | + |
| 68 | +def get_client_size(hwnd): |
| 69 | + rect = wintypes.RECT() |
| 70 | + if not user32.GetClientRect(hwnd, ctypes.byref(rect)): |
| 71 | + return None |
| 72 | + return rect.right - rect.left, rect.bottom - rect.top |
0 commit comments