【问题标题】:Forcing EnumChildWindows to use a global buffer, rather than a local buffer (Python ctypes)强制 EnumChildWindows 使用全局缓冲区,而不是本地缓冲区(Python ctypes)
【发布时间】:2020-12-02 05:07:17
【问题描述】:

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

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

问题:为什么会发生这种情况?如何解决这个问题?

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

(要使示例正常工作,请在启动 Python 解释器后立即切换到带有子项的任意窗口。Windows 的 Groove 音乐应用程序适用于此示例。)

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

【问题讨论】:

标签: python windows winapi scope ctypes


【解决方案1】:

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

调试:

【讨论】:

    【解决方案2】:

    始终声明 .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]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-16
      • 2011-08-14
      • 2014-03-26
      • 1970-01-01
      • 2020-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多