我已經編寫了一個程序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:這是我試過到目前爲止:
- 由於沒有
b.exe
繼承a.exe
;這不起作用。 MSDN明確指出傳遞給CreateProcess的句柄必須是可繼承的。 - 清除stdout/stderr裏面的可繼承標誌
b.exe
:似乎沒有任何作用(如果它確實會讓我感到意外)。 - 讓
ReadDataProc
程序(可讀取兩個管道)考慮除檢查ERROR_BROKEN_PIPE
之外,b.exe
是否實際運行。這當然不起作用(但我只是在後來才意識到),因爲線程在ReadFile調用中被阻塞。
也許嘗試CancelSynchronousIO? – adf88 2010-07-02 09:08:34
@一個df88:根據WaitForMultipleObjects上的MSDN頁面,它可以適用於各種各樣的東西 - 但不是管道。我的實驗似乎證實了這一點:即使在沒有可用數據的情況下,管道的讀取結束也始終發出信號。 CancelSynchronousIO()函數看起來不錯,但它不適合我,因爲它只能在Windows Vista和更新版本上使用。 – 2010-07-02 09:21:16
可能的重複:http://stackoverflow.com/questions/593175/breaking-readfile-blocking-named-pipe-windows-api – adf88 2010-07-03 07:09:47