Я знаю, что много подобных вопросов по этой теме задавалось ранее, но до сих пор я не смог найти решение, которое действительно работает.Я хочу запустить консольную программу из моей программы и записать ее вывод.Моя реализация должна быть совместима с WaitForMultipleObjects()
, т.е. я хочу получать уведомления всякий раз, когда в канале появляются новые данные для чтения.
Моя реализация основана на этот пример из MSDN.Однако мне пришлось немного его изменить, потому что мне нужен перекрывающийся ввод-вывод, чтобы можно было дождаться завершения ReadFile()
.Поэтому я использую именованные каналы, созданные с помощью функции MyCreatePipeEx()
Дейва Харта из здесь .
Это мой настоящий код.Я удалил проверки ошибок для удобства чтения.
HANDLE hReadEvent;
HANDLE hStdIn_Rd, hStdIn_Wr;
HANDLE hStdOut_Rd, hStdOut_Wr;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
OVERLAPPED ovl;
HANDLE hEvt[2];
DWORD mask, gotbytes;
BYTE buf[4097];
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
MyCreatePipeEx(&hStdOut_Rd, &hStdOut_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
MyCreatePipeEx(&hStdIn_Rd, &hStdIn_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
SetHandleInformation(hStdOut_Rd, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(hStdIn_Wr, HANDLE_FLAG_INHERIT, 0);
memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
memset(&siStartInfo, 0, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hStdOut_Wr;
siStartInfo.hStdOutput = hStdOut_Wr;
siStartInfo.hStdInput = hStdIn_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, "test.exe", NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(;;) {
int i = 0;
hEvt[i++] = piProcInfo.hProcess;
memset(&ovl, 0, sizeof(OVERLAPPED));
ovl.hEvent = hReadEvent;
if(!ReadFile(hStdOut_Rd, buf, 4096, &gotbytes, &ovl)) {
if(GetLastError() == ERROR_IO_PENDING) hEvt[i++] = hReadEvent;
} else {
buf[gotbytes] = 0;
printf("%s", buf);
}
mask = WaitForMultipleObjects(i, hEvt, FALSE, INFINITE);
if(mask == WAIT_OBJECT_0 + 1) {
if(GetOverlappedResult(hStdOut_Rd, &ovl, &gotbytes, FALSE)) {
buf[gotbytes] = 0;
printf("%s", buf);
}
} else if(mask == WAIT_OBJECT_0) {
break;
}
}
Проблема с этим кодом заключается в следующем: Как вы можете видеть, я читаю порциями по 4 КБ, используя ReadFile()
, потому что я, очевидно, незнать, сколько данных будет выводить внешняя программа test.exe
.Для этого было предложено здесь :
Чтобы прочитать переменное количество данных из клиентского процесса , просто отправьте запросы на чтение любого удобного вам размера и будьте готовы обрабатывать события чтения, которые короче, чем вы запрашивали.Не интерпретируйте короткую, но ненулевую длину как EOF.Продолжайте выдавать запросы на чтение, пока не получите чтение нулевой длины или ошибку.
Однако это не работает.Объект события, переданный в ReadFile()
как часть структуры OVERLAPPED
, сработает только тогда, когда в буфере будет 4 КБ.Если внешняя программа просто печатает «Hello», событие вообще не сработает.Для фактического запуска hReadEvent
в буфере должно быть 4 КБ.
Поэтому я подумал, что вместо этого я должен читать байт за байтом, и изменил свою программу так, чтобы использовать ReadFile()
следующим образом:
if(!ReadFile(hStdOut_Rd, buf, 1, &gotbytes, &ovl)) {
Однако это тоже не работает.Если я делаю это так, событие чтения вообще не запускается, что меня действительно смущаетПри использовании 4096 байтов событие действительно срабатывает, как только в канале есть 4096 байтов, но при использовании 1 байта оно вообще не работает.
Итак, как мне решить эту проблему?У меня практически нет идей здесь.Нет ли способа вызвать триггер ReadFile()
, когда в канале появляются новые данные?Не может быть так сложно, не так ли?