2010-07-02 87 views
2

我已经编写了一个程序a.exe,该程序使用CreateProcess函数启动我编写的另一个程序b.exe。调用者创建两个管道,并将两个管道的写入端传递给CreateProcess,作为stdout/stderr句柄用于子进程。这几乎与MSDN上的Creating a Child Process with Redirected Input and Output示例相同。如何正确读取子进程的stdout/stderr输出?

由于似乎它不能够使用一个同步调用,它等待过程退出在任stdout或stderr可用数据(WaitForMultipleObjects功能不上的导管的工作),则调用者有两个线程运行,它们都在stdout/stderr管道的读取端执行(阻塞)ReadFile调用;这里是它用于标准输出/标准错误的“读线程程序完全一样的代码(我没有写这个代码我自己,我认为一些同事所做的那样):

DWORD __stdcall ReadDataProc(void *handle) 
{ 
    char buf[ 1024 ]; 
    DWORD nread; 
    while (ReadFile((HANDLE)handle, buf, sizeof(buf), &nread, NULL) && 
      GetLastError() != ERROR_BROKEN_PIPE) { 
     if (nread > 0) { 
      fwrite(buf, nread, 1, stdout); 
     } 
    } 
    fflush(stdout); 
    return 0; 
} 

a.exe然后使用一个简单的WaitForSingleObject调用等到b.exe终止。一旦该调用返回,两个读取线程将终止(因为管道已损坏),并且使用CloseHandle关闭了两个管道的读取结束。现在

,我打的问题是这样的:b.exe威力(根据用户输入)推出其寿命比b.exe本身长的外部进程,守护进程基本上是这样。在这种情况下发生的情况是,stdout/stderr管道的写入结束被继承到该守护进程,因此管道永远不会被破坏。这意味着a.exe中的WaitForSingleObject调用返回(因为b.exe已完成),但在任一管道块上调用CloseHandle,因为两个读取线程仍处于其阻塞的ReadFile调用中。

b.exe返回后,如何解决这个问题,而不用蛮力终止两个读线程(TerminateThread)?如果可能的话,我想避免任何涉及管道和/或过程轮询的解决方案。

UPDATE:这是我试过到目前为止:

  1. 由于没有b.exe继承a.exe;这不起作用。 MSDN明确指出传递给CreateProcess的句柄必须是可继承的。
  2. 清除stdout/stderr里面的可继承标志b.exe:似乎没有任何作用(如果它确实会让我感到意外)。
  3. ReadDataProc程序(可读取两个管道)考虑除检查ERROR_BROKEN_PIPE之外,b.exe是否实际运行。这当然不起作用(但我只是在后来才意识到),因为线程在ReadFile调用中被阻塞。
+0

也许尝试CancelSynchronousIO? – adf88 2010-07-02 09:08:34

+0

@一个df88:根据WaitForMultipleObjects上的MSDN页面,它可以适用于各种各样的东西 - 但不是管道。我的实验似乎证实了这一点:即使在没有可用数据的情况下,管道的读取结束也始终发出信号。 CancelSynchronousIO()函数看起来不错,但它不适合我,因为它只能在Windows Vista和更新版本上使用。 – 2010-07-02 09:21:16

+0

可能的重复:http://stackoverflow.com/questions/593175/breaking-readfile-blocking-named-pipe-windows-api – adf88 2010-07-03 07:09:47

回答

0

是似乎在Windows Vista上(在这里你可以使用CancelSynchronousIO功能之前的Windows版本中,周围用TerminateThread终止读线程没有办法。

一个合适的替代(suggested by adf88)可能是使用异步ReadFile电话,但那是不可能在我的情况(太多变化所需现有的代码)。

5

检查开源库libexecstream http://libexecstream.sourceforge.net/

+0

+1 - 感谢指针!我还没有尝试过,我担心在这个问题上使用一个全新的库会有点矫枉过正,但是研究源代码可能会提供一些见解。感谢您指出这一点。 – 2010-07-04 00:48:28

+2

我很好奇,我检查了 - TerminateThread用于停止阻止ReadFile。 – adf88 2010-07-04 13:41:59

+0

@ adf88:感谢您的检查。为你+1。 :-) – 2010-07-04 20:24:11

2
  1. 使用命名管道和异步ReadFile的

  2. 解析从管道中寻找阅读正文之后的输出(它可能是太复杂了你案件)。
+0

+1:解析管道输出,寻找'流程结束'标记,是一个有创意的想法。 ;-) – 2010-07-21 10:57:43

1

在这种情况下,会发生什么情况是,标准输出/标准错误 管道 书写端继承到守护 的过程,所以管道从未断过。

守护应该关闭他们所继承的文件描述符。

+0

+1:这是一个有趣的观点;对于它的价值,'b.exe'启动的守护程序是客户的程序。然而,在责备他之前,我想要更多的论据来支持这一说法。你有没有链接到某种解释,或者你可以扩大你的答复? – 2010-07-04 20:23:20

+0

成为一个守护进程意味着做一些操作(“守护进程”)和关闭继承的文件描述符就是其中之一(在UNIX中它们通常被从/重定向到/ dev/null而不是关闭)。我相信每一本关于UNIX系统编程的书都会教授这一点(我记得你关心Windows,但守护进程的概念最初来自UNIX)。 (由于长度限制而分裂的评论) – 2010-07-04 21:05:07

+0

对于在线参考,您可以使用维基百科文章https://secure.wikimedia。org/wikipedia/en/wiki/Daemon_(Unix)#Types_of_daemons和它包含的引用。 你可以谷歌了很多HOWTOs来编写守护程序,像这样:http://www.cs.aau.dk/~adavid/teaching/MTP-05/exercises/10/Linux-Daemon_Writting.pdf(第4.6节“闭幕标准文件描述符“) – 2010-07-04 21:05:41

0

设置一些全局标志(布尔exit_flag)和写东西管在A.EXE

相关问题