我正在尝试自动执行用C ++编写的交互式命令行工具。
启动时,二进制文件等待字母S,Q或P(状态,退出或暂停)。它使用非标准的msvcrt函数“getche”来获取键击(例如,而不是get()),而用户不必输入。
我尝试以标准方式与进程通信(写入stdin并使用process.communicate []),但它没有得到输入。经过几个小时尝试不同的事情后,我在Visual Studio中创建了两个小样本项目来复制问题并确保我是理智的(ish)。
这是用于调用二进制文件的python脚本:
import subprocess
import time
cmd = ["test-getch.exe"]
process = subprocess.Popen(cmd, stderr = subprocess.PIPE, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
i = process.stdin
#msvcrt.ungetch('s')
i.write("S\n")
print process.communicate()[0]
i.close()
time.sleep(3)
print "DONE"
这是两个二进制文件。我可以与之沟通的第一个:
#include "stdafx.h"
#include <conio.h>
int _tmain(int argc, _TCHAR* argv[])
{
char response [2];
printf("Enter \"s\":\n");
gets(response);
printf("You entered %s", response);
return 0;
}
我无法与之沟通:
#include "stdafx.h"
#include <conio.h>
int _tmain(int argc, _TCHAR* argv[])
{
int response;
printf("Enter \"a\":\n");
response = getche();
printf("You entered %c", response);
return 0;
}
似乎getche()不会监听stdin并且可能会侦听某种键盘事件。有谁知道如何处理这个?
修改 我还要提到我发现了使用IDA Pro捕获输入的方法。我没有编写我试图自动化的原始二进制文件。它是一个封闭的源工具,所以我无法重新编写它如何接受输入而不修补二进制文件。
我实际上选择了一个相当疯狂的解决方案......我非常了解pydbg,它似乎附加到流程并通过流程工具调用我需要的功能。这完全是矫枉过正,但事后我可以脱离这个过程。让它正常运行。
[1] Pydbg:http://pedram.redhive.com/PyDbg/docs/
答案 0 :(得分:2)
getche
从控制台读取,而不是从标准输入读取。如果您的Python进程在控制台窗口中运行,那么您的子进程仍将尝试从同一控制台读取输入,而不是作为标准输入传入的管道。
有可能创建另一个不可见的控制台窗口,将其附加到子流程,并提供输入,但这非常复杂且容易出错。
我建议您只重写程序,只能从标准输入读取,而不是使用getche()
。如果确实希望让它对按键做出反应而不要求用户按Enter键,那么我建议根据标准输入是否来自终端来改变其行为。如果是,请使用getche
,如果不是,请直接从stdin
阅读。您可以使用_isatty
(或POSIX等效的isatty
对此进行测试;出于某种原因,Microsoft已决定在其运行时中弃用POSIX名称。例如:
int ReadChar()
{
if(_isatty(0))
{
// stdin is a terminal
return _getche();
}
else
{
// stdin is not a terminal
return getchar();
}
}
答案 1 :(得分:2)
C:
#include <stdio.h>
int main(int argc, char *argv[]) {
int response;
printf("Enter \"a\": ");
response = getche();
printf("\nYou entered \"%c\" ", response);
return 0;
}
/* gcc test_getch.c -o test_getch.exe */
的Python:
import subprocess
import win32console
def make_buf(c):
buf = win32console.PyINPUT_RECORDType(win32console.KEY_EVENT)
buf.KeyDown = 1
buf.RepeatCount = 1
buf.Char = c
return buf
con_in = win32console.GetStdHandle(win32console.STD_INPUT_HANDLE)
cmd = ["test_getch.exe"]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
buf = make_buf('a')
con_in.WriteConsoleInput([buf])