views:

1139

answers:

2

I am trying to control a console application (JTAG app from Segger) from Python using the subprocess module. The application behaves correctly for stdout, but stdin doesn't seem to be read. If enable the shell, I can type into the input and control the application, but I need to do this programmatically. The same code works fine for issuing commands to something like cmd.exe.

I'm guessing that the keyboard is being read directly instead of stdin. Any ideas how I can send the application input?

from subprocess import Popen, PIPE, STDOUT
jtag = Popen('"C:/Program Files/SEGGER/JLinkARM_V402e/JLink.exe"', shell=True,
                        universal_newlines=True,
                        stdin=PIPE,
                        stdout=PIPE,
                        stderr=STDOUT)

jtag.stdin.write('usb\n')
jtag.stdin.flush()

print "Stdout:"
while True:
    s = jtag.stdout.readline()
    if not s:
        break
    print s,

jtag.terminate()
+1  A: 
I'm guessing that the keyboard is being read directly instead of stdin

This is a pretty strong assumption and before stitching a solution you should try to verify it somehow. There are different levels of doing this. Actually two I can think of right now:

  • Waiting for keyboard events from the main windows loop. if this is the case then you can simulate a keyboard simply by sending the window the right kind of message. these can be wither WM_KEYDOWN or WM_CHAR or perhaps some other related variants.
  • Actually polling the hardware, for instance using GetAsyncKeyState(). This is somewhat unlikely and if this is really what's going on, I doubt you can do anything to simulate it programatically.

Another take on this is trying to use the on-screen keyboard and see if it works with the application. if it does, figure out how to simulate what it does.

Some tools which might be helpful -

  • Spy++ (comes with Visual Studio) - allows you to see what messages go into a window
  • strace allows you to see what syscalls a process is making.
shoosh
The program does appear to be listening to WM_KEYDOWN / WM_CHAR message events instead looking at stdin.I wasn't aware of StraceNT - thanks!
Eric Holmberg
+2  A: 

As shoosh says, I'd try to verify that the application really is looking for keyboard input. If it is, you can try Win32 message passing, or sending it keyboard input via automation.

For the message passing route, you could use the EnumWindows function via ctypes to find the window you're after, then using PostMessage to send it WM_KEYDOWN messages.

You can also send keyboard input via pywinauto, or the ActiveX control of AutoIt via win32com.

Using AutoIt:

from win32com.client import Dispatch

auto = Dispatch("AutoItX3.Control")
auto.WinActivate("The window's title", "")
auto.WinWaitActive("The window's title", "", 10)

auto.Send("The input")
Ryan Ginstrom
This code works. The only issue is that if a user clicks somewhere, it looses input focus, and I have to open the external terminal instead of keeping it hidden. Any thoughts on how to do this while keeping the child process hidden?
Eric Holmberg
EnumWindows -> PostMessage(hwnd, WM_KEYDOWN, ...) should allow you to keep the child process hidden, if in fact the problem is with the child process reading input from keystrokes, and not stdin.
Ryan Ginstrom