Joh*_*ing 4 c++ windows redirect
我有一个长期运行的基于控制台的应用程序Sender,它使用非缓冲输出将简单文本发送到STDOUT,例如cout <<"Message"<< flush().我想创建一个基于MFC对话框的应用程序(名为Receiver),它启动Sender并可以读取它的输出.接收方还应该能够检测发件人何时死亡,或者能够在发生者希望时杀死发件人.Sender对Reciever一无所知,也无法改变Sender的代码.
我已经问过一个单独的问题,关于最好的方法.我的第一次尝试是为子进程创建带有重定向STDIN和STDOUT的管道,并使用异步ReadFileEx调用来读入Sender的数据.这不能正常工作,因为ReadFileEx函数只触发一次,并且只传输零字节,即使我知道Sender正在发送数据的事实.
我正在创建2个带有重定向STDIN和STDOUT的管道,这个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);
Run Code Online (Sandbox Code Playgroud)
... 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;
Run Code Online (Sandbox Code Playgroud)
我的主循环基于WaitForObjectsEx,置于可警告的等待状态以支持异步文件读取.我正在等待两个手柄:一个指示Sender过早死亡的信号,另一个指示Receiver主线程何时想要Sender死亡.在开始循环之前,我在Sender's STOUT 上启动重叠(异步)文件读取操作.忽略明显的内存泄漏和其他黑客 - 这是说明性的:
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);
// ...
}
Run Code Online (Sandbox Code Playgroud)
上述IO目标公开来自OVERLAPPED:
struct IO : public OVERLAPPED
{
char* buf_;
DWORD buf_size_;
DWORD read_;
ThreadParam* thread_;
HANDLE file_;
};
Run Code Online (Sandbox Code Playgroud)
当重叠的Read函数完成时,我读取传入的数据并生成一个字符串:
void CALLBACK OnFileRead(DWORD err, DWORD bytes, OVERLAPPED* ovr)
{
IO* io = static_cast<IO*>(ovr);
string msg(io->buf_, bytes);
}
Run Code Online (Sandbox Code Playgroud)
Sender什么都不知道Receiver,它使用非常简单但非缓冲的方式将文本发送到控制台.
问题:我知道Sender是将数据发送到STDOUT,但我的OnFileRead函数只被调用一次,并且只传输零字节.
为什么我不能这样接收Sender输出?我有错误,或者我做错了什么?
除了@DyP指出的错误,你假设CreatePipe在重叠模式下打开句柄. 你的假设不正确.Microsoft 记录它:
匿名管道不支持异步(重叠)读写操作.这意味着您不能将ReadFileEx和WriteFileEx函数与匿名管道一起使用.此外,当这些函数与匿名管道一起使用时,将忽略ReadFile和WriteFile的lpOverlapped参数.
(事实上,如果你看里面kernel32.dll,在Windows XP为例,CreatePipe不设置第七参数来低位NtCreateNamedPipeFile;该位是当设置CreateNamedPipe被调用FILE_FLAG_OVERLAPPED.)
寻找Dave Hart的MyCreatePipeEx实施; CreatePipe当需要重叠I/O时,它可以用作替代品.只需更改PipeSerialNumber++为InterlockedIncrement(&PipeSerialNumber)以避免MT代码中的竞争条件.
| 归档时间: |
|
| 查看次数: |
2650 次 |
| 最近记录: |