Перенаправление дочернего процесса stdout через буфер консоли в Windows 10 C ++

Я пытаюсь перенаправить stdoutдочерний процесс в Windows. Оба являются консольными программами. У меня нет исходного кода дочернего процесса, поэтому я не могу заставить его сбросить буфер. Как обсуждалось здесь и здесь , для реализации printfи подобных, среда выполнения C буферизует все, кроме консолей и принтеров. Таким образом, решение, по-видимому, создает буфер экрана консоли, используя, соответственно, достаточно CreateConsoleScreenBuffer. Я использую подход из codeproject .

PROCESS_INFORMATION pi;
HANDLE hConsole;
const COORD origin = { 0, 0 };

// Default security descriptor. Set inheritable. 
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE; // So the child can use it

// Create and initialize screen buffer
hConsole = CreateConsoleScreenBuffer(
    GENERIC_READ | GENERIC_WRITE,       // Desired access       
    FILE_SHARE_WRITE | FILE_SHARE_READ, // share mode to child processes
    &sa,                                // SECURITY_ATTRIBUTES      
    CONSOLE_TEXTMODE_BUFFER,            // Must be this.   
    NULL                                // Reserved. Must be NULL 
);
DWORD dwDummy;
FillConsoleOutputCharacter(hConsole, '', MAXLONG, origin, &dwDummy)

Теперь я направляю stdout ребенка на консоль и запускаю процесс

STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_USESTDHANDLES; // first one prevents cursor from showing loading.
si.hStdOutput = hConsole;

//...
// Get the command line and environmental block
//...

if (! CreateProcess(
    NULL,                         // module name. 
    (char*)command_line.c_str(),  // command line
    NULL,                         // process SECURITY_ATTRIBUTES
    NULL,                         // thread SECURITY_ATTRIBUTES
    TRUE,                         // inherit handles
    NULL,                         // creation flags
    enviros,                      // environmentBlock (enviros=NULL for testing)
    cDir,                         // working directory
    &si,                          // STARTUP_INFO object
    &pi                           // PROCESSINFO
) ){
    auto test = GetLastError();
    CloseHandle(hConsole);
    return false;
}
CloseHandle(pi.hThread);

Затем, в цикле, я могу использовать ReadConsoleOutputCharacterдля захвата вывода, как показано на ссылке codeproject. Это выглядит как

//... some initialization

GetConsoleScreenBufferInfo(hConsole, &csbi);
DWORD count = (csbi.dwCursorPosition.Y - lastpos.Y)*lineWidth + csbi.dwCursorPosition.X - lastpos.X;
LPTSTR buffer = (LPTSTR)LocalAlloc(0, count * sizeof(TCHAR));
ReadConsoleOutputCharacter(hConsole, buffer, count, lastpos, &count);
DWORD dwDummy;
FillConsoleOutputCharacter(hConsole, '', count, lastpos, &dwDummy);

//... Now move the cursor and grab the data from `buffer`

В Windows 7 / 8.1 это отлично подходит для всех программ. В Windows 10 некоторые программы, кажется, обходят поставляемые ручки и печатают непосредственно на родительскую консоль , не позволяя мне захватывать вывод по мере необходимости.

У меня есть дополнительная подсказка. Если я заставляю процесс создавать новое окно консоли, т. Е.

CreateProcess(NULL, (char*)command_line.c_str(), NULL, NULL, TRUE, CREATE_NEW_CONSOLE, enviros, cDir, &si, &pi)

но все же перенаправлять дескрипторы в STARTUPINFOобъекте, новая консоль отобразит одну строку, которая говорит The system cannot write to the specified device, что просто является точной формулировкой кода ошибки Windows ERROR_WRITE_FAULT = 29в документах MSDN . Это происходит только для тех программ, которые не работают должным образом. Другие программы, новая консоль пуста, и они все еще функционируют должным образом.

Моя первая мысль - проблема с разрешениями, но у меня есть широкие открытые разрешения для каталогов соответствующих исполняемых файлов.

Вещи, которые я пробовал

  • Другой компьютер с Windows 10
  • Различные версии среды выполнения MS Visual C ++
  • Включение рабочей директории в CreateProcess
  • Добавление супер-разрешающего DACL к SECURITY_ATTRIBUTESобъекту, переданномуCreateProcess
  • Добавление супер-разрешающего DACL к SECURITY_ATTRIBUTESобъекту, переданномуCreateConsoleScreenBuffer
  • Перенос всего на вновь созданный каталог в моем каталоге пользователей Windows
  • Запуск от имени администратора

Глубже

Спасибо @PaulSanders за ваше предложение.

Чтобы помочь всем, кто может помочь, я сделал доступную модифицированную версию кода RTConsole на странице codeproject . Думаю, он должен скомпилироваться в Visual Studio с помощью только перенацели. Вокруг строки 135, я добавил немного строки к фронту вывода, который принимает ожидаемый путь. Я также предоставил предварительно скомпилированную версию для удобства.

Одним из примеров программного обеспечения, которое не работает, является EWBF-шахтер . Для быстрого тестирования с использованием приведенного выше кода вы можете запустить

RTConsole2.exe "path	oewbf.exe" --help

Вы увидите, что флаг префикса отсутствует в выводе.

С другой стороны, с помощью ccminer вы получите ожидаемое поведение при запуске

RTConsole2.exe "path	occminer.exe" --help

c++,winapi,

4

Ответов: 1


Глубже

Спасибо @PaulSanders за ваше предложение.

Чтобы помочь всем, кто может помочь, я сделал доступную модифицированную версию кода RTConsole на странице codeproject . Думаю, он должен скомпилироваться в Visual Studio с помощью только перенацели. Вокруг строки 135, я добавил немного строки к фронту вывода, который принимает ожидаемый путь. Я также предоставил предварительно скомпилированную версию для удобства.

Одним из примеров программного обеспечения, которое не работает, является EWBF-шахтер . Для быстрого тестирования с использованием приведенного выше кода вы можете запустить

RTConsole2.exe "path	oewbf.exe" --help

Вы увидите, что флаг префикса отсутствует в выводе.

С другой стороны, с помощью ccminer вы получите ожидаемое поведение при запуске

RTConsole2.exe "path	occminer.exe" --help
14
2 принят

Новая консольная реализация в Windows 10 имеет ошибку, в которой высокоуровневый WriteConsoleи WriteFileнеактивный буфер экрана вместо этого всегда записывается в активный буфер экрана. Низкий уровень WriteConsoleOutput[Character]работает правильно. Также работает устаревшая консоль. Вы можете включить устаревшую консоль в диалоговом окне свойств.


Обратите внимание, что процесс не может использовать унаследованный дескриптор экранного буфера в консоли родителя, если он выделяет новую консоль из-за CREATE_NEW_CONSOLEфлага. Попытка написать файл экранного буфера не удастся, поскольку она не привязана к подключенной консоли вызывающего (например, экземпляр conhost.exe).

Консольные файлы, которые связаны, включают в себя «CON», «CONIN $», «CONOUT $» и новый экранный буфер CreateConsoleScreenBuffer. Существуют также несвязанные входные и выходные файлы консоли, которые устанавливаются как стандартные дескрипторы при распределении новой консоли (например, через AllocConsole()). Эти ручки обрабатывают входной буфер и активный буфер экрана любой подключенной консоли [*]. Обратите внимание, что процесс может иметь дескрипторы для файлов консоли, которые привязаны к нескольким консолям и могут переключаться между консолями AttachConsole.

Обратите также внимание на то, что некоторые программы открывают «CONOUT $» вместо того, чтобы писать StandardOutputи StandardErrorобрабатывать, особенно если им требуется консоль вместо того, что может быть стандартными дескрипторами (например, файл с каналом или диском). В этом случае не достаточно , чтобы установить hStdOutputв STARTUPINFO. Вы должны временно активировать новый экранный буфер SetConsoleActiveScreenBuffer. Это не влияет на стандартные ручки звонящего. Он устанавливает активный буфер экрана в прикрепленной консоли, и это означает, что открывается «CONOUT $». Предыдущий экранный буфер может быть восстановлен после завершения дочернего процесса или после того, как вы знаете, что ребенок уже открыт и записан в новый экранный буфер.


[*] В Windows 8+ эти файлы консоли реализованы драйвером устройства condrv.sys и открываются на « Device ConDrv». Они называются соответственно «Консоль», «CurrentIn», «CurrentOut», «ScreenBuffer», «Input» и «Output». Имя файла для самого подключения консоли - «Подключиться». Внутренне последний открыт , как процесс ConsoleHandle, который используется неявно консоли API в некоторых случаях (например GetConsoleWindow, GetConsoleCPи GetConsoleTitle).

C ++, WinAPI,
Похожие вопросы