0

I'm trying to get EnumChildWindow to pass a global-scope buffer as the lparam parameter to its callback.

For some reason, instead of simply using the buffer I pass it, the callback instead creates another buffer with a different address. (Let's call this the "local buffer.")

Question: Why does this happen? How can this be fixed?

See the minimal reproducible example below.

(For the example to work, immediately after starting the Python interpreter, switch to an arbitrary window with children. Windows' Groove Music app works fine for this example.)

import ctypes
import time


user32 = ctypes.windll.user32

def callback(hwnd, buffer):
    print(f"The address of the callback's buffer is {buffer}")
    return True

# Define a function pointer to our callback.
prototype = ctypes.WINFUNCTYPE(
    ctypes.c_bool, ctypes.c_int, ctypes.c_char * 80
    )
win_callback = prototype(callback)

global_buff = ctypes.create_string_buffer(80)

time.sleep(2)
parent_hwnd = user32.GetForegroundWindow()

print(f'The address of the global buffer is {global_buff}')
user32.EnumChildWindows(parent_hwnd, win_callback, global_buff)
print(f'The address of the global buffer is {global_buff}')
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
Asker
  • 1,299
  • 2
  • 14
  • 31
  • Check [\[SO\]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)](https://stackoverflow.com/questions/58610333/c-function-called-from-python-via-ctypes-returns-incorrect-value/58611011#58611011) for generic stuff, and [\[SO\]: Get the title of a window of another program using the process name (@CristiFati's answer)](https://stackoverflow.com/questions/31278590/get-the-title-of-a-window-of-another-program-using-the-process-name/31280850#31280850) to see if you can find something related to your question. – CristiFati Dec 02 '20 at 15:22

2 Answers2

1

EnumChildWindow can achieve the expected effect in C++. The reason why it fails in python, I guess is that the parameter type of user32.EnumChildWindows do not match.

You can use win32gui.EnumChildWindows instead. It works for me.

import ctypes
import time
import win32gui

user32 = ctypes.windll.user32

def callback(hwnd, buffer):
    print('callback:')
    print(buffer)
    return 1

global_buff = ctypes.create_string_buffer(80)

time.sleep(2)
parent_hwnd = user32.GetForegroundWindow()

print(f'The address of the global buffer is {global_buff }')
win32gui.EnumChildWindows(parent_hwnd, callback, global_buff)
print(f'The address of the global buffer is {global_buff }')

Debug:

enter image description here

Updated:

Change the parameter types in ctypes.WINFUNCTYPE to match their types. Refer to the example of user32.EnumChildWindows below, you can get the value passed in.

import ctypes
import time

user32 = ctypes.windll.user32

def callback(hwnd, buffer):
    array = (ctypes.c_char*80).from_address(buffer)
    print(array.value)
    return True

# Define a function pointer to our callback.
prototype = ctypes.WINFUNCTYPE(
    ctypes.c_bool, ctypes.c_int, ctypes.c_void_p
    )
win_callback = prototype(callback)

global_buff = ctypes.create_string_buffer(b'hello',80)
print(global_buff.value)
time.sleep(2)
parent_hwnd = user32.GetForegroundWindow()

print(f'The address of the global buffer is {global_buff}')
user32.EnumChildWindows(parent_hwnd, win_callback, global_buff)
print(f'The address of the global buffer is {global_buff}')

Debug:

enter image description here

Strive Sun
  • 5,988
  • 1
  • 9
  • 26
1

Always declare .argtypes and .restype. It helps check for errors. You can also "fudge" the types if they are compatible. For a callback with a generic pasthrough LPARAM parameter, you can use a ctypes.py_object type and pass any Python object:

import ctypes
import time
from ctypes import wintypes as w

WNDENUMPROC = ctypes.WINFUNCTYPE(w.BOOL,w.HWND,ctypes.py_object)

user32 = ctypes.WinDLL('user32')
user32.GetForegroundWindow.argtypes = ()
user32.GetForegroundWindow.restype = w.HWND
user32.EnumChildWindows.argtypes = w.HWND,WNDENUMPROC,ctypes.py_object
user32.EnumChildWindows.restype = w.BOOL

@WNDENUMPROC
def callback(hwnd, obj):
    print(f'callback: {hwnd} {obj}')
    obj.append(hwnd)
    return True

obj = []

time.sleep(2)
parent_hwnd = user32.GetForegroundWindow()
print(obj)
user32.EnumChildWindows(parent_hwnd,callback,obj)
print(obj)
[]
callback: 1051954 []
callback: 528896 [1051954]
callback: 921048 [1051954, 528896]
callback: 984872 [1051954, 528896, 921048]
callback: 1575202 [1051954, 528896, 921048, 984872]
callback: 2949434 [1051954, 528896, 921048, 984872, 1575202]
callback: 1182662 [1051954, 528896, 921048, 984872, 1575202, 2949434]
callback: 1444604 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662]
callback: 1772044 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604]
callback: 396900 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044]
callback: 2230612 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900]
callback: 854282 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612]
callback: 592926 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282]
callback: 790020 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926]
callback: 1444764 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926, 790020]
callback: 1182928 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926, 790020, 1444764]
callback: 1183292 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926, 790020, 1444764, 1182928]
callback: 592960 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926, 790020, 1444764, 1182928, 1183292]
callback: 723948 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926, 790020, 1444764, 1182928, 1183292, 592960]
callback: 461712 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926, 790020, 1444764, 1182928, 1183292, 592960, 723948]
callback: 1313044 [1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926, 790020, 1444764, 1182928, 1183292, 592960, 723948, 461712]
[1051954, 528896, 921048, 984872, 1575202, 2949434, 1182662, 1444604, 1772044, 396900, 2230612, 854282, 592926, 790020, 1444764, 1182928, 1183292, 592960, 723948, 461712, 1313044]
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251