强制 EnumChildWindows 使用全局缓冲区,而不是本地缓冲区(Python ctypes)

Forcing EnumChildWindows to use a global buffer, rather than a local buffer (Python ctypes)

我正在尝试让 EnumChildWindow 将全局范围缓冲区作为 lparam 参数传递给它的回调。

出于某种原因,回调没有简单地使用我传递给它的缓冲区,而是创建了另一个具有不同地址的缓冲区。 (我们称之为“本地缓冲区”。)

问题:为什么会出现这种情况?如何解决?

请参阅下面的最小可重现示例。

(为了使示例正常工作,在启动 Python 解释器后,立即切换到带有子项的任意 window。Windows' Groove Music 应用程序在此示例中运行良好。 )

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}')

EnumChildWindow在C++中可以达到预期的效果。之所以在python失败,我猜测是user32.EnumChildWindows的参数类型不匹配。

您可以改用win32gui.EnumChildWindows。它对我有用。

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 }')

调试:

更新:

更改 ctypes.WINFUNCTYPE 中的参数类型以匹配它们的类型。参考下面user32.EnumChildWindows的例子,可以得到传入的值

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}')

调试:

始终声明 .argtypes.restype。它有助于检查错误。如果类型兼容,您也可以“捏造”这些类型。对于具有通用直通 LPARAM 参数的回调,您可以使用 ctypes.py_object 类型并传递任何 Python 对象:

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]