【问题标题】:process.communicate and getche() failsprocess.communicate 和 getche() 失败
【发布时间】:2011-12-28 08:36:28
【问题描述】:

我正在尝试自动执行用 C++ 编写的交互式命令行工具。

启动时,二进制文件会等待字母 S、Q 或 P(状态、退出或暂停)。它使用非标准的 msvcrt 函数“getche”来获取击键(例如,而不是 get()),而无需用户按 Enter。

我尝试以标准方式与进程通信(写入标准输入并使用 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() 似乎没有在标准输入上侦听,并且可能在侦听某种键盘事件。有谁知道如何处理这个问题?

编辑: 我还应该提到我发现了使用 IDA Pro 捕获输入的方法。我没有编写我试图自动化的原始二进制文件。它是一个闭源工具,所以我无法在不修补二进制文件的情况下重写它如何接受输入。

我实际上选择了一个相当疯狂的解决方案......我非常了解 pydbg,而且似乎附加到进程并通过进程检测调用我需要的函数是可行的。这完全是矫枉过正,但之后我可以脱离这个过程。并让它正常运行。

[1] Pydbg:http://pedram.redhive.com/PyDbg/docs/

【问题讨论】:

  • 使用process.communicate("S\n") 而不是i.write()。如果您使用.communicate(),请勿触摸process.stdin。虽然它对你的情况没有帮助。

标签: python windows subprocess popen


【解决方案1】:

如果您可以修改被调用程序的行为,Adam Rosenfield 的回答是一种明智的做法。否则,如果您确实需要写入控制台输入缓冲区,请尝试 PyWin32 的 win32console 模块。也就是说,我不确定当标准输出通过管道时如何使字符回显部分正常工作。它最终打印到行首。

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

【讨论】:

  • 感谢您的推荐。今晚我会考虑使用 win32console。
【解决方案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();
    }
}

【讨论】:

  • 我编辑了原始问题以包含我没有编写原始二进制文件的事实。我发现了程序如何使用 IDA Pro 接受输入,并创建了示例项目来消除处理整个二进制文件的复杂性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-08
  • 2011-11-20
  • 2012-01-20
  • 1970-01-01
  • 2015-06-17
  • 2017-01-04
相关资源
最近更新 更多