键盘事件未使用pywin32发送到窗口

时间:2018-12-14 10:48:32

标签: python windows python-2.7 pywin32

我编写了一个代码,该代码可从我想要的任何程序中获取HWND。因此,如果您提出要求,那就是我怎么得到的。

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

但是确实可以。它确实启动了设备管理器,但没有将向下箭头键发送给程序,至少没有任何反应。

如果我用记事本窗口的hwnd代码更改hwndMain编号,则该代码确实起作用并发送向下箭头键

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

1 个答案:

答案 0 :(得分:2)

每个 Win 窗口可以具有 0 或更多子窗口,并且每个子窗口也可以具有0个或多个其子窗口自己的,依此类推...因此每个窗口可能都有一整棵子树。

关于窗户的更多信息,不仅仅吸引眼球。用户可能会看一个(顶部)窗口,并想象它的树以某种方式看起来,而实际上树可能看起来完全不同(更复杂),因为可能有些窗口不可见。

在将消息发送到窗口并期望发生某种行为时,必须将消息发送到确切的窗口(或发送到其祖先之一,该祖先的设计方式是转发它),否则该消息将被忽略(因为错误窗口无法处理此类消息)。
在我们的情况下,这意味着 WM_KEYDOWN (或 WM_CHAR )消息应发送至:

  • 保存记事本
  • 文本的( Edit )窗口
  • 用于保存设备管理器
  • 的设备列表的( TreeView )窗口

您使用的是[ActiveState.Docs]: win32gui.GetWindow,其中包含[MS.Docs]: GetWindow function的状态(对于 GW_CHILD ):

  

如果指定的窗口是父窗口,则检索到的句柄在Z顺序的顶部标识子窗口。否则,检索到的句柄为 NULL 。该功能仅检查指定窗口的子窗口。它不检查后代窗口。

巧合,因为记事本将消息发送到其第一个 st 子作品,因为该子作品变成了 Edit 上面我提到的窗口(除了这个子 Notepad 仅具有另一个子窗口,即 StatusBar ,就是这样,这些窗口中没有一个子窗口具有自己的)。

另一方面,对于设备管理器来说,事情并不是那么简单。如您所见,其结构更为复杂(例如,可见 ToolBar 窗口)。按照建议,为了使用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个窗口句柄( np_wnd dm_wnd )进行了硬编码。显然,它们将无效(自从我关闭窗户以来,它们在我的机器上不再有效),并且需要更改它们的值
  • 为了找到窗口的句柄(及其子元素),我使用了 Spy ++ [MS.Docs]: How to: Start Spy++),它是 VStudio 的一部分,但我肯定还有很多其他类似的应用程序

输出

e:\Work\Dev\StackOverflow\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

从输出中看到, TreeView 窗口是第 7 th 个子(第7 个) 设备管理器窗口中的child :)),这意味着它们之间有6个中间(和不可见)窗口(忽略该消息)。

尽管代码可以解决相关窗口的问题,但目前没有适用于任何窗口的配方(或者,如果我不知道,则不知道)。我必须提到,我已经尝试通过查看树的子窗口来确定它感兴趣的子窗口:

  • 名称
  • 班级
  • 样式(该区域的 MS 文档非常差)
    • 扩展风格
  • 位置(相对于其父位置)
  • SendMessage 的返回码

但是我找不到任何可以将其与其他窗口区分开的东西。我唯一注意到的是,对于 Notepad ,所需的窗口是枚举的1 st 子级,而对于 Device Manager ,则是7 < sup 一个,所以我基于这个事实( child_index )进行了过滤,但是我认为它完全不可靠

或者,根本不进行过滤,而是将消息发送到树中的所有子窗口,但这可能会产生不良影响,因为可能会有其他窗口对此消息做出响应。例如,设备管理器树由〜30 个子窗口组成。

最后,我还要提及一些窗口(例如 Chrome 的网络浏览器)具有自己的Windows系统,因此这些都不起作用。