У меня есть долго работающее консольное приложение Sender, которое отправляет простой текст в STDOUT, используя небуферизованный вывод, такой как cout << "Message" << flush ().Я хочу создать диалоговое приложение MFC (с именем Receiver), которое запускает Sender и может читать его вывод.Получатель также должен иметь возможность определять, когда отправитель умер, или иметь возможность убивать отправителя, если он этого хочет.Отправитель ничего не знает о Получателе, и я не могу изменить код Отправителя. </p>
Я задал отдельный вопрос о наилучшем способе сделать это.Моей первой попыткой было создание каналов с перенаправленными STDIN и STDOUT для дочернего процесса и использование асинхронных вызовов ReadFileEx для чтения данных отправителя.Это не работает правильно, потому что функция ReadFileEx запускается только один раз, и только с нулевыми байтами, даже если я знаю, что Sender
отправляет данные.
Я создаю 2 канала с перенаправленным STDINи STDOUT, ala этот пример MS :
// allow the child process to inherit handles
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(sa);
sa.bInheritHandle = 1;
// create pipes with rerouted stdin & stdout
CreatePipe(&handles[h_Child_StdOut_Read], &handles[h_Child_StdOut_Write], &sa, 0);
SetHandleInformation(handles[h_Child_StdOut_Read], HANDLE_FLAG_INHERIT, 0);
CreatePipe(&handles[h_Child_StdIn_Read], &handles[h_Child_StdIn_Write], &sa, 0);
SetHandleInformation(handles[h_Child_StdIn_Read], HANDLE_FLAG_INHERIT, 0);
... Receiver
, затем запускается Sender
через CreateProcess ():
// create child process
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(si);
si.hStdOutput = handles[h_Child_StdOut_Write];
si.hStdInput = handles[h_Child_StdIn_Read];
si.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess( 0, "Sender.EXE", 0, 0, 1, 0, 0, 0, &si, &pi);
handles[h_Child_Process] = pi.hProcess;
handles[h_Child_Thread] = pi.hThread;
Мой основной цикл основан на WaitForObjectsEx, помещенном в состояние ожидания с предупреждением для поддержки чтения асинхронного файла.Я жду двух дескрипторов: один сигнализирует о преждевременной смерти Sender
, а другой сигнализирует о том, что основной поток Receiver
хочет, чтобы Sender
умер.Перед началом цикла я запускаю перекрывающуюся (асинхронную) операцию чтения файла на STDOUT Sender
.Не обращайте внимания на очевидные утечки памяти и другие хаки - это иллюстративно:
vector<HANDLE> wait_handles;
wait_handles.push_back(handles[h_Die_Sig]);
wait_handles.push_back(handles[h_Child_Process]);
for( bool cont = true; cont; )
{
IO* io = new IO;
memset(io, 0, sizeof(IO));
io->buf_size_ = 16 * 1024;
io->buf_ = new char[io->buf_size_];
memset(io->buf_, 0, io->buf_size_);
io->thread_ = ¶m;
io->file_ = handles[h_Child_StdOut_Read];
if( !ReadFileEx(io->file_, io->buf_, io->buf_size_, io, OnFileRead) )
{
DWORD err = GetLastError();
string err_msg = util::strprintwinerr(err);
}
DWORD rc = WaitForMultipleObjectsEx(wait_handles.size(), &wait_handles[0], FALSE, INFINITE, TRUE);
// ...
}
Объект IO
, описанный выше, публично получен из OVERLAPPED
:
struct IO : public OVERLAPPED
{
char* buf_;
DWORD buf_size_;
DWORD read_;
ThreadParam* thread_;
HANDLE file_;
};
Когда перекрывающееся чтениеФункция завершается, я считываю входящие данные и генерирую строку:
void CALLBACK OnFileRead(DWORD err, DWORD bytes, OVERLAPPED* ovr)
{
IO* io = static_cast<IO*>(ovr);
string msg(io->buf_, bytes);
}
Sender
ничего не знает о Receiver
, и отправляет текст на консоль, используя очень простые, но не буферизованные средства.
Проблема: я знаю, что Sender
отправляет данные на свой STDOUT, но моя OnFileRead
функция вызывается только один раз и только с нулевыми переданными байтами.
Почему я не могуполучить вывод Sender
таким образом?У меня ошибка или я что-то не так делаю?