Доброго всем времени суток.
Наткнулся на проблему, и никак не могу найти решение.
Суть: из моей программы нужно перехватывать консольный вывод от дочернего процесса. Делается через Pipe. Работало все замечательно до тех пор, пока не понадобилось получать раздельно вывод в StdOutput и StdError. Для этого создал второй пайп и подсунул его в hStdError. И вот тут случилось непредвиденное. При выполнении ReadFile из пайпа виснет мое приложение (вроде как понятно — ждет пока в пайп хоть что-то будет записано). Но дочернее приложение также виснет, но уже на вызове WriteFile. И все. Намертво.
Написал воспроизводимый пример. Компилировал VS2005 и VS2008. Результат один — зависание. Ощущение, что происходит deadlock на дескрипторе пайпа. (Обидно к тому же, что функция, которая виснет при записи в пайп на стороне дочернего процесса из msvcrt называется _write_no_lock).
Может кто сталкивался с подобной проблемаой или знает пути ее решения?
вот исходный код "родительского процесса":
#include <iostream>
#include <windows.h>
#include <conio.h>
using namespace std;
char buffer[256];
char szTestExe[] = "test8.exe"; //имя файла для дочернего процесса
void main()
{
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
HANDLE hStdOutRead, hStdOutWrite;
HANDLE hStdErrRead, hStdErrWrite;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(buffer, sizeof(char)*256);
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true;
sa.lpSecurityDescriptor = NULL;
CreatePipe(&hStdOutRead, &hStdOutWrite, &sa, 4096);
CreatePipe(&hStdErrRead, &hStdErrWrite, &sa, 4096);
SetHandleInformation(hStdOutRead, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(hStdErrRead, HANDLE_FLAG_INHERIT, 0);
si.cb = sizeof(STARTUPINFO);
si.wShowWindow = SW_SHOW;
si.hStdError = hStdErrWrite; //Если для stderr и stdout указать один
si.hStdOutput = hStdOutWrite; //пайп, то все работает нормально
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.dwFlags = STARTF_USESTDHANDLES;
if (CreateProcess(szTestExe, NULL, NULL, NULL, true,
CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(hStdErrWrite);
CloseHandle(hStdOutWrite);
BOOL res = false;
DWORD dwBytesRead = 0;
cout << "---===== starting =====---" << endl;
do
{
//Здесть происходит зависание. ReadFile не возвращает управление.
res = ReadFile(hStdOutRead, buffer, 255, &dwBytesRead, NULL);
if (res && dwBytesRead > 0)
{
buffer[dwBytesRead] = '\0';
cout << buffer;
}
} while(res);
cout << "---=========== errors ===========---" << endl;
do
{
res = ReadFile(hStdErrRead, buffer, 255, &dwBytesRead, NULL);
if (res && dwBytesRead > 0)
{
buffer[dwBytesRead] = '\0';
cout << buffer;
}
} while(res);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(hStdOutRead);
CloseHandle(hStdErrRead);
} else
{
cout << "unable to start process" << endl;
}
cout << "Done" << endl;
_getch();
}
А вот для дочернего процесса:
#include <stdio.h>
#include <stdlib.h>
#define BUF_SIZE 1024
char buff[BUF_SIZE];
void main()
{
for (int i= 0; i < BUF_SIZE; i++)
buff[i] = (char)(rand()%(127-20))+20;
buff[BUF_SIZE-1] = '\0';
for (int i = 0; i < 100; i++)
{
fprintf(stdout, "%s %d\n", "HELLO WORLD FROM TEST", i);
//при выводе в stderr в определенный момент происходит зависание на
//функции WriteFile (когда происходить flush буфера, примерно при записи
//4096 байт во временный буфер
fprintf(stderr, "%s", buff);
}
fflush(stderr);
fflush(stdout);
}
_setvbuf(stdout, ...
_setvbuf(stderr, ...
Здравствуйте, Spaun2002, Вы писали:
S>S> //Здесть происходит зависание. ReadFile не возвращает управление.
S> res = ReadFile(hStdOutRead, buffer, 255, &dwBytesRead, NULL);
S> ...
S> res = ReadFile(hStdErrRead, buffer, 255, &dwBytesRead, NULL);
S>
Попробуй использовать PeekNamedPipe() для проверки наличия байтов в трубочке. Таким образом ты будеш посасывать сразу из двух трубочек. Частоту опроса подберешь сам. Возможно понадобиться увеличить значение в последнем параметре CratePipe(). В цикле (res) попробуй заменить на (res && dwBytesRead).
S>S> for (int i= 0; i < BUF_SIZE; i++)
S>
i < BUF_SIZE — 1