Как перехватить стандартный вывод из другого процесса в Win32 без задержки? - PullRequest
6 голосов
/ 24 июля 2010

То, что я хотел бы сделать, аналогично тому, что Visual Studio делает в своем окне вывода или других редакторах в их окнах инструментов: запустить другой процесс B из моего процесса A и записать его вывод stdout / stderr.

До сих пор я работал с CreatePipe(), но по какой-то причине вывод B не приходит в B сразу после записи. Он ведет себя как заполнение буфера какого-то рода, и когда он заполнен, все содержимое буфера сразу попадает в A. Я написал свою собственную тестовую программу, которая что-то выводит и делает fflush(stdout) сразу после этого. Затем вывод напрямую приходит к A. Но я не могу изменить код всех процессов B, которые я хотел бы использовать таким образом. Попытка промыть трубу от А также не работает.

Как это должно работать?

Мой код инициализации и код потребления:

 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
 sa.bInheritHandle = TRUE;
 sa.lpSecurityDescriptor = NULL;

 err = CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &sa, stdouthistory);
 if (err == 0)
     return 1;
 err = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
                       GetCurrentProcess(), &hChildStdoutRdDup , 0,
                       FALSE,
                       DUPLICATE_SAME_ACCESS);
 if (err == 0)
     return 3;
 CloseHandle(hChildStdoutRd);

 DWORD a, b, c;
 a = PIPE_READMODE_BYTE | PIPE_NOWAIT;
 b = 0;
 c = 0;
 SetNamedPipeHandleState(hChildStdoutRdDup, &a, &b, &c);

 err = CreatePipe(&hChildStdinRd, &hChildStdinWr, &sa, stdinhistory);
 if (err == 0)
     return 1;
 err = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
                       GetCurrentProcess(), &hChildStdinWrDup , 0,
                       FALSE,
                       DUPLICATE_SAME_ACCESS);
 if (err == 0)
     return 4;
 CloseHandle(hChildStdinWr);

 a = PIPE_READMODE_BYTE | PIPE_NOWAIT;
 b = 0;
 c = 0;

 ZeroMemory(&si,sizeof(STARTUPINFO));
 si.cb = sizeof(STARTUPINFO);
 si.dwFlags = STARTF_USESTDHANDLES;
 si.wShowWindow = SW_SHOW;

 si.hStdOutput = hChildStdoutWr;
 si.hStdError = hChildStdoutWr;
 si.hStdInput = hChildStdinRd;

 ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );

 err = CreateProcess(0, this->cmdline, 0, 0, true, CREATE_NO_WINDOW, 0, 0, &si, &pi);
 if (err == 0)
     return 4;

Потребление:

 DWORD avail;
 unsigned int ofs = 0;
 if (PeekNamedPipe(hChildStdoutRdDup, NULL, 0, NULL, &avail, NULL))
 {
     if (avail != 0)
     {
         int err = ReadFile(hChildStdoutRdDup, s + ofs, slen, &threadbuffern, 0);
                           // Consume ...
     }
 }

Edit: Я только что нашел этот вопрос: Постоянно читал из STDOUT внешнего процесса в Ruby . Это та же проблема, но в контексте Ruby. К сожалению, решение состояло в том, чтобы использовать некоторую библиотеку Ruby, которая просто заставляет ее работать. Как эта библиотека делает это? Что эквивалентно в Win32 / C ++?

1 Ответ

3 голосов
/ 24 июля 2010

Вы не можете этого сделать.Если вывод не был сброшен в процессе нарушения, он на самом деле не был записан в stdout.То есть операционная система на самом деле даже не получила данные от целевого процесса.

Это не какая-то внутренняя задержка с каналами, а то, что программы, которые вы отслеживаете, фактически не написали ихв канал еще.

Вы должны заметить точно такое же поведение для командной строки при выполнении указанных программ, потому что командная строка использует то же решение канала, которое вы используете.Если вы этого не сделаете, это потому, что рассматриваемые программы обнаруживают, что они записывают в дескриптор файла, а не в дескриптор консоли, и выполняют дополнительную буферизацию.

...