1

I'm trying to automate an application (let's call it app.exe) by sending keystrokes to it. When its main window is open and has focus, doing ALT+F opens the File menu. I have inspected all the messages posted to the window with Spy++, and I have carefully studied all the parameters.

Then I tried to replicate exactly the same behaviour with PostMessage (to be sure I'm not sending to the wrong window, I'm even looping on all hWnd associated to the relevant process ID):

import win32con, win32gui, win32process, win32api, subprocess, time

def get_hwnds_for_pid(pid):
    def callback (hwnd, hwnds):
        if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
            _, found_pid = win32process.GetWindowThreadProcessId(hwnd)
            if found_pid == pid:
                hwnds.append(hwnd)
        return True
    hwnds = []
    win32gui.EnumWindows(callback, hwnds)
    return hwnds

app = subprocess.Popen(["notepad.exe"])

time.sleep(2.0)

for hwnd in get_hwnds_for_pid(app.pid):
    win32gui.SetForegroundWindow(hwnd)
    win32api.PostMessage(hwnd, win32con.WM_SYSKEYDOWN, 0x12, 0x20380001) # ALT
    win32api.PostMessage(hwnd, win32con.WM_SYSKEYDOWN, 0x46, 0x20210001) # F
    time.sleep(1.0)
    win32api.PostMessage(hwnd, win32con.WM_KEYUP, 0x46, 0xC0210001)
    win32api.PostMessage(hwnd, win32con.WM_KEYUP, 0x12, 0x20380001)

Problem: this works when the app is notepad.exe. But it fails with another particular application I'm using (a regular Win32 application, which uses QWidget for the main window).

To be more precise, I see the _ underline flashing under the first letter of the menu during one second (so the simulated ALT is recognized ; the usual behaviour in Windows application is that the underlined F appears when you hold the ALT button down) but the F is not recognized.

Once again, I have sent with PostMessage exactly the same messages to the window as what I have observed with Spy++ when I did the hotkey manually with the real keyboard.

Question: what could prevent a given software app.exe to receive messages posted/injected from another application (=here, my Python script)?

Basj
  • 41,386
  • 99
  • 383
  • 673
  • The obvious explanation is simply that posting such messages is not a supported way to fake input. To fake input use SendInput. This is discussed daily in this very site. – David Heffernan Mar 04 '19 at 19:18
  • @DavidHeffernan As explained [here](https://stackoverflow.com/q/54882751/1422096), the goal is to send keystrokes to a specific window (in background), but do something else in the meantime. As far as I know, `SendInput` will send keystrokes to the current active window, so it wouldn't work to send keystrokes to a background window. That's why I used the solution https://stackoverflow.com/a/38888131 with `PostMessage` for this purpose. – Basj Mar 04 '19 at 19:40
  • It's not a solution though. You cannot reliably fake input that way. As has been covered so very many times before here. It's kinda tedious. – David Heffernan Mar 04 '19 at 20:19
  • 2
    Why don't you use [UIAutomation](https://learn.microsoft.com/en-us/windows/desktop/winauto/entry-uiauto-win32)? It doesn't require hacks like faking input and also works if the target application is running in the background. AFAIK there are wrappers for Python too. – zett42 Mar 04 '19 at 21:11
  • @zett42 I haven't found a simple example showing how to do it with Python yet. For UIAutomation to work, does it require that the application to be automated has enabled this feature? Or is it possible to use it with any application? – Basj Mar 05 '19 at 08:08
  • As I have told you before, there is no single method that works for all programs. To continue this conversation without knowledge of the target program is rather pointless. If you want to check whether or not UIAutomation works for your program, make a test using a language other than Python. The easiest place to start will be C#. – David Heffernan Mar 05 '19 at 10:57
  • @DavidHeffernan Do you mean this kind of UIAutomation? https://github.com/yinkaisheng/Python-UIAutomation-for-Windows. It seems that they give an example with notepad.exe there. – Basj Mar 05 '19 at 12:50
  • @DavidHeffernan UIAutomation you're speaking about seems to **not** be an option because it has to give the focus to the window to be automated. [Try to run this code which uses UIAutomation](https://pastebin.com/rUx6vUp4): it will always go back to the notepad window, impossible to do anything else (example: using browser) at the same time! – Basj Mar 05 '19 at 14:54
  • I guess that makes sense given that the way to fake input is to use `SendInput`. I always thought that UIAutomation offered an alternative if the target app supports it, but perhaps that's not the case. – David Heffernan Mar 05 '19 at 15:07
  • UIAutomation defines a set of interfaces to enumerate and manipulate controls **without sending actual input** to the controls. That's the whole point of it, to provide an actual programmatic interface. The sample you linked to uses `SendKeys` which is not part of this interface. – zett42 Mar 05 '19 at 16:39
  • To answer your earlier question: The target app must support it, but most apps do (some better, some worse). Apps using only standard controls (or controls derived from them) can usually be automated without much issues, custom controls can be problematic. – zett42 Mar 05 '19 at 16:45
  • @zett42 Thanks for the clarification. Would you have a Python example showing how to use it? (it seems that my example using `SendKeys` is not appropriate). – Basj Mar 05 '19 at 19:36
  • I don't have one. So far I have only used [UI Automation PowerShell Extensions](https://archive.codeplex.com/?p=uiautomation). – zett42 Mar 05 '19 at 21:23
  • To give you an idea, here is an [example to invoke the File/Open menu item of Notepad](https://pastebin.com/DxEswrp2) using the Powershell extension. – zett42 Mar 05 '19 at 21:55

0 Answers0