【问题标题】:How to wrap a windows interactive console program for automation如何包装 Windows 交互式控制台程序以实现自动化
【发布时间】:2012-08-01 03:36:03
【问题描述】:

我在 Windows 中有一个交互式控制台程序。我需要按'e''c'之类的击键到cmd窗口。人工操作方便,但程序自动化很难。

现在,我想包装控制台程序,以便在其他程序(如 python)中更容易操作。

但是,控制台程序使用 'getch()' 来获取键盘输入,这不是标准输入。所以,我不能简单地将密钥发送到标准输入。

有人遇到过这个问题吗? 谢谢。

    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')

编辑:

最后,我使用 win32 SendMessage 函数来完成任务。我分叉了一个新的子进程,然后隐藏它,获取它的 hWnd 和 pid。

代码如下:

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

【问题讨论】:

  • 谢谢。这是我在 stackoverflow 上的第一篇文章:P
  • 您也许可以像stackoverflow.com/a/11786712/1514681 中那样使用子流程模块做一些事情,但我不确定,我以前从未使用过它,但我昨天遇到了这个答案,我猜是模块或许也能做到这一点。

标签: python subprocess pipe stdin getch


【解决方案1】:

据我所知,getch()确实使用标准输入。可能是应用程序确实收到了该输入,但您没有收到它的输出?这是一个常见问题,因为如果输出没有指向终端,它通常会被缓冲。您可以调整程序以在每行之后刷新其输出吗?

我发现您的 python 代码存在许多问题:您没有为 stderr 创建管道,并且 proc 变量似乎是 start 方法的本地变量。

您可能希望暂时保留从 python 进程继承的子进程输出。这应该会抑制缓冲并排除ThreadWorker 中的任何错误。

【讨论】:

  • accroding to stackoverflow.com/questions/8117055/… 我实际上选择了一个相当疯狂的解决方案......我非常了解 pydbg,似乎附加到进程并通过进程检测调用我需要的函数是可行的.这完全是矫枉过正,但之后我可以脱离这个过程。并让它正常运行。似乎 getche() 没有在标准输入上监听,并且可能监听某种键盘事件。有人知道如何处理吗?
【解决方案2】:

您需要做的就是:

写一个文件,每一步都换行:

例如:

第 1 步
第 2 步
第三步

然后像这样调用程序:

程序.exe

nuff 说!!

【讨论】:

    猜你喜欢
    • 2017-01-25
    • 1970-01-01
    • 2011-02-08
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 2020-07-01
    • 2016-12-27
    相关资源
    最近更新 更多