强制 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]
我正在尝试让 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]