2

I have an interactive console program in windows. I need to press keystroke like 'e' 'c' to the cmd window. It's convenient for human operating , but very difficult for program automation.

And now, I would like to wrap the console program, in order to make it more easy to be manipulated in other program like python.

However, the console program uses 'getch()' to get keyboard input , which is not standard input. So, I can't simply send key into stdin.

Did anyone ever come across this problem? Thanks.

    class ThreadWorker(threading.Thread):
        def __init__(self, callable, *args, **kwargs):
            super(ThreadWorker, self).__init__()
            self.callable = callable
            self.args = args
            self.kwargs = kwargs
            self.setDaemon(True)

        def run(self):
            try:
                self.callable(*self.args, **self.kwargs)
            except Exception, e:
                print e

        def start(self):
            global running
            running = True
            threading.Thread.start(self)

    def console_presskey(char):
        proc.stdin.write(char)
        proc.stdin.flush()

    def start():
        def worker(pipe):
            while running:
                line = pipe.readline()
                if line == '': 
                    break
                else:
                    print line,

        proc = Popen("something.exe",stdout=PIPE,stdin=PIPE)

        stdout_worker = ThreadWorker(worker, proc.stdout)
        stderr_worker = ThreadWorker(worker, proc.stderr)
        stdout_worker.start()
        stderr_worker.start()


    if __name__ == "__main__":
        start()
        sleep(2)
        console_presskey('e')
        sleep(2)
        console_presskey('c')

EDIT:

Finally, I use win32 SendMessage function to get things done. I forked a new subprocess, then hide it , get its hWnd and pid.

here is the code:

import threading
import re
import os
from subprocess import Popen, PIPE,STDOUT,CREATE_NEW_CONSOLE,STARTUPINFO,STARTF_USESHOWWINDOW,SW_HIDE
from time import sleep
import win32process,win32con,win32gui,win32api

TargetPower = 'N/A'
Mode = 'N/A'
Channel = 'N/A'
con_stdin = ''
con_stdout = ''
stdout_worker = ''
stdin_woker = ''
running = False
proc = ''
Console_hwnd = ''
#status = 'NotStarted' # or Running 
class ThreadWorker(threading.Thread):
    def __init__(self, callable, *args, **kwargs):
        super(ThreadWorker, self).__init__()
        self.callable = callable
        self.args = args
        self.kwargs = kwargs
        self.setDaemon(True)

    def run(self):
        try:
            self.callable(*self.args, **self.kwargs)
        except Exception, e:
            print e

    def start(self):
        global running
        running = True
        threading.Thread.start(self)
def get_hwnds_for_pid (pid):
    def callback (hwnd, hwnds):
        #if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
        _, found_pid = win32process.GetWindowThreadProcessId(hwnd)
        #print hwnd
        if found_pid == pid:
            hwnds.append(hwnd)
        return True
    hwnds = []
    win32gui.EnumWindows(callback, hwnds)
    #if hwnds == []:
        #raise 
    return hwnds

def sendkey(char):
    global Console_hwnd
    hwnd = Console_hwnd[0]
    code = ord(char)
    win32api.SendMessage(hwnd, win32con.WM_CHAR, code, 0)
    print '[*]',char,'Pressed.'

    #Another Keypress example. only works with keycode
    #win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_F9, 0)
    #print char,code,"down"
    #win32api.PostMessage(hwnd, win32con.WM_KEYUP, code, 0)
    #print char,code,"up"
def sendesc():
    global Console_hwnd
    hwnd = Console_hwnd[0]
    win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_ESCAPE, 0)
    print '[*]',"Esc down"
    win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_ESCAPE, 0)
    print '[*]',"Esc up"

def start():
    def worker(pipe):
        global TargetPower
        global Mode
        global Channel
        global running
        while running:
            line = pipe.readline()
            if line == '':
                break
            elif line.startswith('|') or line.startswith('==='):
                pass
            elif line.startswith("Operating in"):
                info = line
                for i in range(7):
                    info += pipe.readline()
                    #print 'ART> '+info,
                try:
                    TargetPower = eval(re.search(r'(Output|Target) (power|Power) = .{4}',info).group(0).split('=')[1])
                    #27.0
                    Mode = re.search(r'gOffRate = (.+?),',info).group(1).lstrip().rstrip()
                    #6MBps
                    Channel = re.search(r'channel ([\d.]+GHz),',info).group(1)
                    #2.412GHz
                except Exception,e:
                    TargetPower = 'N/A'
                    Mode = 'N/A'
                    Channel = 'N/A'
                    print e
            elif line =='\r\n':
                print '>',
            else:
                print 'ART>'+line,
        print 'worker done.'
    global proc
    si = STARTUPINFO()
    si.dwFlags |= STARTF_USESHOWWINDOW
    si.wShowWindow = SW_HIDE
    #proc = Popen("art.bat",stdout=PIPE,creationflags=CREATE_NEW_CONSOLE) #qt works!
    proc = Popen("art.bat",stdout=PIPE,creationflags=CREATE_NEW_CONSOLE,startupinfo=si)
    #proc = Popen("art.bat",stdout=PIPE,startupinfo=si)  #hidden
    #proc = Popen("cmd")  #for test
    sleep(2)
    print '[*] pid: ',proc.pid

    global Console_hwnd
    Console_hwnd = get_hwnds_for_pid(proc.pid)
    print '[*] hwnd:',Console_hwnd[0]

    global stdout_worker
    global stderr_worker
    stdout_worker = ThreadWorker(worker, proc.stdout)
    stderr_worker = ThreadWorker(worker, proc.stderr)
    stdout_worker.start()
    stderr_worker.start()


def stop():
    global stdout_worker
    global stderr_worker
    global running
    print 'stop'
    global proc
    sendesc()
    sendesc()
    sendesc()
    Popen("taskkill /F /T /PID %i"%proc.pid , shell=True)
    try:
        running = False
        TargetPower = 'N/A'
        Mode = 'N/A'
        Channel = 'N/A'
    except Exception,e:
        print e

if __name__ == "__main__":
    start()
    sleep(1)
    sendkey('e')
    sleep(1)
    sendkey('c')
    sleep(10)
    stop()
    while True:
        pass
scateu
  • 21
  • 3
  • thanks . This is my first post on stackoverflow :P – scateu Aug 04 '12 at 20:39
  • You might be able to do something with the subprocess module like in http://stackoverflow.com/a/11786712/1514681 but I'm not sure, I've never used it before but I ran across this answer yesterday and am guessing the module might be able to do this too. – LJNielsenDk Dec 08 '13 at 12:37

2 Answers2

0

As far as I know, getch() does use standard input. COuld it be that the application did receive that input, but you don't receive its output? That's a common problem, as output often gets buffered if it is not directed at a terminal. Can you adjust the program to flush its output after each line?

I see a number of problems with your python code: you don't create a pipe for stderr, and the proc variable appears to be local to the start method.

You might want to leave the subprocess output inherited from the python process for the time being. That should suppress buffering and rules out any errors in the ThreadWorker as well.

MvG
  • 57,380
  • 22
  • 148
  • 276
  • accroding to http://stackoverflow.com/questions/8117055/process-communicate-and-getche-fails I've actually chosen a rather insane solution that works... I know pydbg quite well and it seems that attaching to the process and calling the functions I need via process instrumentation works. It's totally overkill but I can detach from the process afterwards. and have it run normally. It appears that getche() doesn't listen on stdin and probably listens for some kind of keyboard event. Anyone know how to deal with this? – scateu Aug 04 '12 at 05:38
0

All you need to do is:

Write a file with each step in a new line:

for instance:

step 1
step 2
step 3

then call the program like this:

program.exe < input.txt

nuff said!!