2

I'm trying to get a list of all visible windows using ctypes in Python3.3

But with the code i have, not a single window is returned. The EnumWindows function fails, and the returned error code is 0.

import ctypes

user32 = ctypes.windll.user32

def worker(hwnd, lParam):
    length = user32.GetWindowTextLengthW(hwnd) + 1
    buffer = ctypes.create_unicode_buffer(length)
    user32.GetWindowTextW(hwnd, buffer, length)
    print("Buff: ", repr(buffer.value))

a = ctypes.WINFUNCTYPE(ctypes.c_bool,
                       ctypes.POINTER(ctypes.c_int),
                       ctypes.POINTER(ctypes.c_int))(worker)

if not user32.EnumWindows(a, True):
    print("Err: ", ctypes.windll.kernel32.GetLastError())

Here is the current output:

Buff:  ''
Err:  0

And this is what i expected:

Buff: 'Python 3.3.2 shell'
Buff: 'test.py - C:\Users\...'
[...]

Could you point me in the right direction to achieve this? Thanks in advance.

gcq
  • 937
  • 3
  • 13
  • 19

1 Answers1

8

The callback needs to return TRUE to continue the enumeration. Your callback is implicitly returning None, which is false. The following revised version should do what you want:

import ctypes
from ctypes import wintypes

WNDENUMPROC = ctypes.WINFUNCTYPE(wintypes.BOOL,
                                 wintypes.HWND,
                                 wintypes.LPARAM)
user32 = ctypes.windll.user32
user32.EnumWindows.argtypes = [
    WNDENUMPROC,
    wintypes.LPARAM]
user32.GetWindowTextLengthW.argtypes = [
    wintypes.HWND]
user32.GetWindowTextW.argtypes = [
    wintypes.HWND,
    wintypes.LPWSTR,
    ctypes.c_int]

def worker(hwnd, lParam):
    length = user32.GetWindowTextLengthW(hwnd) + 1
    buffer = ctypes.create_unicode_buffer(length)
    user32.GetWindowTextW(hwnd, buffer, length)
    print("Buff: ", repr(buffer.value))
    return True

cb_worker = WNDENUMPROC(worker)
if not user32.EnumWindows(cb_worker, 42):
    raise ctypes.WinError()

The HWND handle type is an alias for c_void_p. LPARAM is an integer that's the same storage size as a pointer. It's defined as follows in wintypes:

if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
    WPARAM = ctypes.c_ulong
    LPARAM = ctypes.c_long
elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
    WPARAM = ctypes.c_ulonglong
    LPARAM = ctypes.c_longlong
Eryk Sun
  • 33,190
  • 5
  • 92
  • 111
  • Are .argtypes always necessary? Or you put them to show what types the functions take? – gcq Jan 17 '14 at 09:03
  • @gcq: They aren't strictly necessary, but they are extremely useful, as they enable sanity checks for type compatibility before the call is sent off to the hazards of C land and for some types they also enable automatic conversion of the passed parameters to the correct type. The benefits outweigh the costs (adding one more line to your code) by a wide margin. – blubberdiblub Sep 03 '17 at 14:58