Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save aont/13b61366ca97291572be13cf0f45edd1 to your computer and use it in GitHub Desktop.

Select an option

Save aont/13b61366ca97291572be13cf0f45edd1 to your computer and use it in GitHub Desktop.

Enumerating Visible Windows on Windows with ctypes

  • Enumerates all top-level visible windows using EnumWindows, then filters to windows that are visible and have a non-empty title (IsWindowVisible, GetWindowText*).
  • Enriches each window with process and metadata by collecting the window class (GetClassNameW), PID (GetWindowThreadProcessId), and executable path (OpenProcess + QueryFullProcessImageNameW).
  • Extracts and interprets window style flags via GetWindowLongPtrW/GetWindowLongW to report key attributes such as DISABLED, TOPMOST, TOOLWIN, and APPWIN alongside raw STYLE/EXSTYLE hex values.
import ctypes
from ctypes import wintypes
import sys
try:
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
except Exception:
pass
user32 = ctypes.WinDLL("user32", use_last_error=True)
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
EnumWindowsProc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
# --- GWL / style constants ---
GWL_STYLE = -16
GWL_EXSTYLE = -20
WS_DISABLED = 0x08000000
WS_EX_TOPMOST = 0x00000008
WS_EX_TOOLWINDOW = 0x00000080
WS_EX_APPWINDOW = 0x00040000
# --- prototypes ---
user32.EnumWindows.argtypes = [EnumWindowsProc, wintypes.LPARAM]
user32.EnumWindows.restype = wintypes.BOOL
user32.IsWindowVisible.argtypes = [wintypes.HWND]
user32.IsWindowVisible.restype = wintypes.BOOL
user32.GetWindowTextLengthW.argtypes = [wintypes.HWND]
user32.GetWindowTextLengthW.restype = ctypes.c_int
user32.GetWindowTextW.argtypes = [wintypes.HWND, wintypes.LPWSTR, ctypes.c_int]
user32.GetWindowTextW.restype = ctypes.c_int
user32.GetClassNameW.argtypes = [wintypes.HWND, wintypes.LPWSTR, ctypes.c_int]
user32.GetClassNameW.restype = ctypes.c_int
user32.GetWindowThreadProcessId.argtypes = [wintypes.HWND, ctypes.POINTER(wintypes.DWORD)]
user32.GetWindowThreadProcessId.restype = wintypes.DWORD
kernel32.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
kernel32.OpenProcess.restype = wintypes.HANDLE
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
kernel32.CloseHandle.restype = wintypes.BOOL
kernel32.QueryFullProcessImageNameW.argtypes = [
wintypes.HANDLE, wintypes.DWORD, wintypes.LPWSTR, ctypes.POINTER(wintypes.DWORD)
]
kernel32.QueryFullProcessImageNameW.restype = wintypes.BOOL
# --- GetWindowLongPtrW (32/64対応) ---
if ctypes.sizeof(ctypes.c_void_p) == 8:
user32.GetWindowLongPtrW.argtypes = [wintypes.HWND, ctypes.c_int]
user32.GetWindowLongPtrW.restype = ctypes.c_longlong
def get_window_long_ptr(hwnd, index) -> int:
return int(user32.GetWindowLongPtrW(hwnd, index))
else:
user32.GetWindowLongW.argtypes = [wintypes.HWND, ctypes.c_int]
user32.GetWindowLongW.restype = ctypes.c_long
def get_window_long_ptr(hwnd, index) -> int:
return int(user32.GetWindowLongW(hwnd, index))
def get_window_title(hwnd: int) -> str:
length = user32.GetWindowTextLengthW(hwnd)
if length <= 0:
return ""
buf = ctypes.create_unicode_buffer(length + 1)
user32.GetWindowTextW(hwnd, buf, length + 1)
return buf.value
def get_window_class(hwnd: int) -> str:
buf = ctypes.create_unicode_buffer(256)
n = user32.GetClassNameW(hwnd, buf, len(buf))
return buf.value if n > 0 else ""
def get_pid(hwnd: int) -> int:
pid = wintypes.DWORD(0)
user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
return int(pid.value)
def get_process_image_path(pid: int) -> str:
hproc = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
if not hproc:
return ""
try:
size = wintypes.DWORD(4096)
buf = ctypes.create_unicode_buffer(size.value)
if kernel32.QueryFullProcessImageNameW(hproc, 0, buf, ctypes.byref(size)):
return buf.value
return ""
finally:
kernel32.CloseHandle(hproc)
def get_styles(hwnd: int) -> tuple[int, int]:
style = get_window_long_ptr(hwnd, GWL_STYLE)
exstyle = get_window_long_ptr(hwnd, GWL_EXSTYLE)
return style, exstyle
def has_flag(value: int, flag: int) -> bool:
return (value & flag) == flag
def enum_windows():
results = []
@EnumWindowsProc
def callback(hwnd, lparam):
if not user32.IsWindowVisible(hwnd):
return True
title = get_window_title(hwnd)
if not title:
return True
cls = get_window_class(hwnd)
pid = get_pid(hwnd)
image = get_process_image_path(pid)
style, exstyle = get_styles(hwnd)
results.append((hwnd, pid, cls, title, image, style, exstyle))
return True
if not user32.EnumWindows(callback, 0):
raise ctypes.WinError(ctypes.get_last_error())
return results
if __name__ == "__main__":
for hwnd, pid, cls, title, image, style, exstyle in enum_windows():
disabled = has_flag(style, WS_DISABLED)
topmost = has_flag(exstyle, WS_EX_TOPMOST)
toolwin = has_flag(exstyle, WS_EX_TOOLWINDOW)
appwin = has_flag(exstyle, WS_EX_APPWINDOW)
print(
f"HWND=0x{hwnd:08X} PID={pid:<6} "
f"DISABLED={int(disabled)} TOPMOST={int(topmost)} TOOLWIN={int(toolwin)} APPWIN={int(appwin)} "
f"STYLE=0x{style:08X} EXSTYLE=0x{exstyle:08X} "
f"CLASS={cls} TITLE={title} EXE={image}"
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment