subprocess上重叠的ReadFileEx'redirect标准输出不会触发

我有一个长期运行的基于控制台的应用程序发件人,它使用非缓冲输出(如cout <<“Message”<< flush())将简单文本发送到STDOUT。 我想创build一个基于MFC对话框的应用程序(名为Receiver)启动发件人并可以读取它的输出。 接收者也应该能够检测到发送者已经死亡,或者如果愿意的话可以杀死发送者。 发件人对Reciever一无所知,我不能改变发件人的代码。

我已经问了一个单独的问题 ,最好的方法来做到这一点。 我的第一个尝试是创buildpipe道redirectSTDIN和STDOUT的subprocess,并使用asynchronousReadFileEx调用来读取发件人的数据。 这是不正常工作的,因为ReadFileEx函数只会触发一次,只有传输零字节,即使我知道Sender正在发送数据的事实。

我正在创build2个redirectSTDIN和STDOUT的pipe道,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然后继续通过CreateProcess()启动Sender

 // 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,放置在一个可警告的等待状态,以支持读取asynchronous文件。 我在等待两个句柄:一个是Sender过早死亡的信号,另一个是Receiver的主线程要Sender者死亡的信号。 在开始循环之前,我在Sender者的STDOUT上启动一个重叠的(asynchronous)文件读取操作。 忽略明显的内存泄漏和其他黑客 – 这是说明性的:

 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_ = &param; 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_; }; 

当重叠的Read函数完成时,我读取传入的数据并生成一个string:

 void CALLBACK OnFileRead(DWORD err, DWORD bytes, OVERLAPPED* ovr) { IO* io = static_cast<IO*>(ovr); string msg(io->buf_, bytes); } 

SenderReceiver一无所知,它使用非常简单的但不缓冲的方式将文本发送到控制台。

问题:我知道Sender正在向其STDOUT发送数据,但是我的OnFileRead函数只被调用一次,并且只传输零字节。

为什么我不能以这种方式接收Sender的输出? 我有一个错误,或者我做错了什么?

我认为你有一个错字:

 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); 

改变最后一个

 SetHandleInformation(handles[h_Child_StdIn_Write], HANDLE_FLAG_INHERIT, 0); 

这也是他们在MSDN的例子。

除了@DyP指出的错误之外,你假设CreatePipe以重叠模式打开了句柄。 你的假设是不正确的 。 微软将其文档 :

匿名管道不支持异步(重叠)读写操作。 这意味着您不能对匿名管道使用ReadFileEx和WriteFileEx函数。 另外,当这些函数与匿名管道一起使用时,ReadFile和WriteFile的lpOverlapped参数将被忽略。

(事实上​​,如果您查看kernel32.dll ,例如在Windows XP上, CreatePipe不会将第七个参数的低位设置为NtCreateNamedPipeFile ;使用FILE_FLAG_OVERLAPPED调用CreateNamedPipe设置该位)。

寻找Dave Hart的MyCreatePipeEx实现; 当需要重叠I / O时,它可以用作CreatePipe替代品。 只需将PipeSerialNumber++更改为InterlockedIncrement(&PipeSerialNumber)以避免MT代码中的竞争状况。