2010-07-02 178 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

相關問題