malonn
malonn

Reputation: 111

Python ctypes EnumThreadWindows failing with error 87 (ERROR_INVALID_PARAMETER)

I can't seem to get EnumThreadWindows to work. It keeps failing with error 87. Code:

error = ctypes.WinDLL('Kernel32').GetLastError
enum_func = ctypes.WINFUNCTYPE(wintypes.BOOL,
                               wintypes.HWND,
                               wintypes.LPARAM)

def callback(hwnd, lParam):
    length = ctypes.WinDLL('User32').GetWindowTextLengthW(hwnd) + 1
    buf = ctypes.create_unicode_buffer(length)
    ctypes.WinDLL('User32').GetWindowTextW(hwnd, buf, length)
    print(buf.value)

worker = enum_func(callback)
test = ctypes.WinDLL('User32').EnumThreadWindows(6000, worker, None)
print(error(test))

I've tried pid = wintypes.DWORD(6000), test = ctypes.WinDLL('User32').EnumThreadWindows(pid.value, worker, None) to no avail. What am I doing wrong?

Upvotes: 0

Views: 239

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 177461

Here's working code. Make sure to pass a valid thread ID.

You might be interested in the fact that an LPARAM can be anything, including a python object, so if you pass a Python object it can be manipulated in the callback:

import ctypes
from ctypes import wintypes
from collections import namedtuple

Window = namedtuple('Window','hwnd title')

WNDENUMPROC = ctypes.WINFUNCTYPE(wintypes.BOOL,
                                 wintypes.HWND,
                                 ctypes.py_object) # to allow any Python object.

u32 = ctypes.WinDLL('user32',use_last_error=True) # to ensure GetLastError was captured

# Declaring arguments and return type helps catch errors and support 64-bit.
# HWND is 64-bit on 64-bit Python, and could get truncated if left to ctypes default
# of c_int (32-bit).  This code works on Python 2.7 and 3.9.
u32.GetWindowTextLengthW.argtypes = wintypes.HWND,
u32.GetWindowTextLengthW.restype = ctypes.c_int
u32.GetWindowTextW.argtypes = wintypes.HWND,wintypes.LPWSTR,ctypes.c_int
u32.GetWindowTextW.restype = ctypes.c_int
u32.EnumThreadWindows.argtypes = wintypes.DWORD,WNDENUMPROC,ctypes.py_object # to pass Python object
u32.EnumThreadWindows.restype = wintypes.BOOL

@WNDENUMPROC # decorator makes this a ctypes-compatible function
def callback(hwnd, lParam):
    length = u32.GetWindowTextLengthW(hwnd) + 1
    buf = ctypes.create_unicode_buffer(length)
    u32.GetWindowTextW(hwnd, buf, length)
    lParam.append(Window(hwnd,buf.value)) # append data to the callback parameter
    return True # return True to continue enumeration

result = [] # A python object
if u32.EnumThreadWindows(6332, callback, result): # 6332 was a thread in my explore.exe
    for wnd in result:  # list results when enumeration complete
        print(wnd)
else:
    print('error:',ctypes.get_last_error()) # print error of EnumThreadWindows.
                                            # Python could use a Win32 function that fails in
                                            # between the ctypes call and calling GetLastError
                                            # directly.

Output:

Window(hwnd=65832, title='')
Window(hwnd=65838, title='')
Window(hwnd=131174, title='')
Window(hwnd=65682, title='')
Window(hwnd=65678, title='')
Window(hwnd=65826, title='Program Manager')
Window(hwnd=196928, title='MSCTFIME UI')
Window(hwnd=65680, title='Default IME')

Upvotes: 2

Related Questions