process.communicate и getche () не работают - PullRequest
3 голосов
/ 14 ноября 2011

Я пытаюсь автоматизировать выполнение интерактивного инструмента командной строки, написанного на C ++.

При запуске двоичный файл ожидает буквы S, Q или P (Status, Quit или Pause).Он использует нестандартную функцию 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/

Ответы [ 2 ]

2 голосов
/ 14 ноября 2011

Ответ Адама Розенфилда - разумный подход, если вы можете изменить поведение вызываемой программы. В противном случае, если вам действительно необходимо выполнить запись в буфер ввода консоли, попробуйте модуль win32console PyWin32. Тем не менее, я не уверен, как заставить часть эха персонажа работать правильно, когда stdout передается по каналу. Это заканчивается печатью в начале строки.

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])
2 голосов
/ 14 ноября 2011

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();
    }
}
...