pywin32 未将键盘事件发送到 window

Keyboard event not sent to window with pywin32

我写了一段代码,可以从我想要的任何程序中获取 HWND。如果你问的话,这就是我得到 hwnd 的方式。

以下代码应调出设备管理器并将向下箭头发送到程序。

但确实如此。它确实会调出设备管理器,但不会向程序发送向下箭头键,至少没有任何反应。

如果我用记事本的 hwnd 代码更改 hwndMain 号码 window,代码确实有效并发送向下箭头键

import win32api
import win32con
import win32gui
import time

hwndMain = 133082
hwndChild = win32gui.GetWindow(hwndMain, win32con.GW_CHILD)
win32gui.SetForegroundWindow(hwndMain)
time.sleep(1)

win32api.SendMessage(hwndChild, win32con.WM_CHAR, 0x28, 0)

编辑

我试过了

win32api.SendMessage(hwndChild, win32con.WM_CHAR, win32con.WM_KEYDOWN, 0)

而不是

win32api.SendMessage(hwndChild, win32con.WM_CHAR, 0x28, 0)

但这也不管用。

我在 python 2.7

每个Winwindow可以有0或更多child windows,每个 child windows 也可以有 0 个或多个 children,依此类推...所以每个 window可能有一整棵children.

关于 windows 的内容比我们看到的要多。用户可能会看着一个(顶部)window 并想象它的树以某种方式看起来,而实际上那棵树看起来可能完全不同(更复杂),因为可能有一些 windows不可见。

将消息发送到 window 并期望发生某种行为时,必须将消息 发送到确切的 window(或它的祖先之一以这种方式设计来转发它),否则消息将被简单地忽略(因为 wrong window 不处理那种消息).
在我们的例子中,这意味着应该发送 WM_KEYDOWN(或 WM_CHAR)消息至:

  • 保存记事本
  • 文本的(Edit)window
  • 保存设备管理器
  • 设备列表的(TreeView)window

您使用的 [ActiveState.Docs]: win32gui.GetWindow, which wraps [MS.Docs]: GetWindow function 表示(对于 GW_CHILD):

The retrieved handle identifies the child window at the top of the Z order, if the specified window is a parent window; otherwise, the retrieved handle is NULL. The function examines only child windows of the specified window. It does not examine descendant windows.

巧合,对于记事本发送消息到其1stchild 有效,因为 child 变成了我上面提到的 Edit window(除此之外 child,Notepad 只有另一个 StatusBar,仅此而已,none 个 windows 有任何 children自己)。

另一方面,对于 设备管理器,事情就没那么简单了。如您所见,它的结构更复杂(例如 ToolBar window 是可见的)。按照建议,为了使用 windows,我使用 [MS.Docs]: EnumChildWindows function.

code.py:

#!/usr/bin/env python3

import sys
import pywintypes
import win32gui
import win32con


def enum_child_proc(wnd, param):
    print("    Handling child 0x{:08X} - [{:}] - 0x{:08X}".format(wnd, win32gui.GetWindowText(wnd), win32gui.GetParent(wnd)))
    if param[0] >= 0:
        if param[1] == param[0]:
            win32gui.SendMessage(wnd, win32con.WM_KEYDOWN, win32con.VK_DOWN, 0)
            return 0
        param[1] += 1


def handle_window(wnd, child_index=-1):
    print("Handling 0x{:08X} - [{:}]".format(wnd, win32gui.GetWindowText(wnd)))
    cur_child = 0
    param = [child_index, cur_child]
    try:
        win32gui.EnumChildWindows(wnd, enum_child_proc, param)
    except pywintypes.error as e:
        if child_index < 0 or e.args[0]:
            raise e


def main():
    np_wnd = 0x01DB1EE2  # Notepad handle
    dm_wnd = 0x000E2042  # Device Manager handle

    handle_window(np_wnd, child_index=0)
    handle_window(dm_wnd, child_index=6)


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

备注:

  • 我硬编码了 2 个 window 句柄(np_wnddm_wnd)。显然,它们将无效(它们在我的机器上也不再有效,因为我关闭了 windows),并且它们的值需要更改
  • 为了找到 window 的句柄(以及它的一些 children),我正在使用 Spy++[MS.Docs]: How to: Start Spy++),它是 VStudio 的一部分,但我确信还有大量其他类似的应用程序

输出:

e:\Work\Dev\Whosebug\q053778227>"e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

Handling 0x01DB1EE2 - [Untitled - Notepad]
    Handling child 0x01811FA4 - [] - 0x01DB1EE2
Handling 0x000E2042 - [Device Manager]
    Handling child 0x00621A5A - [] - 0x000E2042
    Handling child 0x01991F44 - [Device Manager] - 0x00621A5A
    Handling child 0x01691F3E - [] - 0x01991F44
    Handling child 0x000C20B0 - [] - 0x01691F3E
    Handling child 0x004D2000 - [] - 0x000C20B0
    Handling child 0x004420CA - [] - 0x004D2000
    Handling child 0x01191F20 - [] - 0x004420CA

从输出结果可以看出,TreeViewwindow是7thchild(第7child:)设备管理器window的意思它们之间有 6 个中介(并且不可见)windows(忽略该消息)。

尽管代码对有问题的 windows 有效,目前没有适用于任何 window 的配方(或者如果有,我对此一无所知)。我必须提到,我试图通过查看树来确定 child window 感兴趣的树:

  • 姓名
  • Class
  • 风格(MS doc在这方面很差)
    • 扩展样式
  • 位置(相对于其parent)
  • SendMessage的return代码

但我找不到任何可以将其与其他 windows 区分开来的东西。我唯一注意到的是,对于 Notepad,所需的 window 是枚举的 1st child,而设备管理器是第7st,所以我根据这个事实进行了过滤(child_index), 但我认为完全不可靠.

作为替代方案,可以完全不进行过滤,并将消息发送到树中的所有 child windows,但这可能会产生不良影响,因为可能还有其他windows 回复该消息。例如设备管理器树由~30childwindows.

组成

最后,我还想提一下,有些windows(网络浏览器,如Chrome),有自己的windows 系统,因此 none 将起作用。