[英]Weird behaviour when passing grandchild output of CreateProcess to the grandparent through the child
我正在開發一個應用程序 A,它調用CreateProcess
來運行子進程 B,而 B 又調用createProcess
來運行子進程 C。 The output of C should be passed through by B and then read by A. When I run B myself (outside of A), it works fine and the output of C is shown on the console. 但是當我運行 A 時,它會看到 B 的所有 output除了從 C 傳遞的內容。 顯然,我正在為孫子或類似的東西打一個特殊的案例。
為了進行實驗,我創建了一個簡單的示例test.c
,它以數字作為參數。 它輸出對應於數字的字母( a
表示 1 等)。 然后它使用CreateProcess
遞歸地啟動相同的程序,但減少了參數。 它以<N: OUTPUT>
的格式從子級傳遞所有 output ,其中N
是數字參數。
我希望看到以下結果:
> .\test.exe 1
a
> .\test.exe 2
b<2: a>
> .\test.exe 3
c<3: b><3: <2: a>>
我意識到由於緩沖最后一個 output 也可能類似於c<3: b<2: a>>
---重要的是 output a
嵌套在<2:>
塊中並且<2:>
塊嵌套在<3:>
塊中:這表明孫子的 output 首先由子接收,然后傳遞給父。
我的程序對於 arguments 1
和2
運行正確,但是對於3
我始終得到這個 output:
c<3: a><3: b<2: >>
這對我來說根本沒有意義。 就好像來自孫子的 output 直接由最頂層的進程接收,因為我們得到<3: a>
。 而且我也不知道如何解釋<3: b<2: >>
。
如何修復此示例,以便孫子的 output 首先被孩子看到,然后傳遞給父母?
#include <stdio.h>
#include <windows.h>
static counter; /* the digit argument */
static HANDLE stdout_read;
static void error (char *s)
{
fprintf (stderr,"error: %s\n",s);
exit (1);
}
/* Thread function to pass through output */
static DWORD WINAPI copy_out_func (LPVOID unused)
{
CHAR buffer[512];
for (;;) {
DWORD n_bytes;
if (!ReadFile (stdout_read,buffer,sizeof (buffer),&n_bytes,NULL) || n_bytes==0)
break;
printf ("<%d: ",counter);
WriteFile (GetStdHandle (STD_OUTPUT_HANDLE),buffer,n_bytes,&n_bytes,NULL);
printf (">");
}
return 1;
}
int main(int argc, TCHAR *argv[])
{
SECURITY_ATTRIBUTES sa;
HANDLE stdout_write,copy_out_thread;
char cmd[64]=".\\test.exe";
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL bSuccess = FALSE;
if (argc != 2)
error ("Usage: .\\test.exe N");
counter = argv[1][0]-'0';
if (counter <= 0)
return 0; /* end recursion */
printf ("%c",'a'+counter-1);
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!CreatePipe (&stdout_read,&stdout_write,&sa,0))
error ("CreatePipe");
if (!SetHandleInformation (stdout_read,HANDLE_FLAG_INHERIT,0))
error ("SetHandleInformation");
ZeroMemory (&pi,sizeof (PROCESS_INFORMATION));
ZeroMemory (&si,sizeof (STARTUPINFO));
si.cb = sizeof (STARTUPINFO);
si.hStdOutput = stdout_write;
si.hStdError = stdout_write;
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
si.dwFlags |= STARTF_USESTDHANDLES;
cmd[10]=' ';
cmd[11]='0' + (counter-1);
cmd[12]='\0';
if (!CreateProcess (NULL,cmd,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi))
error ("CreateProcess");
CloseHandle (stdout_write);
copy_out_thread = CreateThread (NULL,0,copy_out_func,NULL,0,NULL);
CloseHandle (pi.hThread);
WaitForSingleObject (pi.hProcess,INFINITE);
WaitForSingleObject (copy_out_thread,INFINITE);
return 0;
}
我認為這可能是因為祖父母的某些句柄被孫子錯誤地繼承了。 因此,我創建了一個更長的示例,使用帶有LPPROC_THREAD_ATTRIBUTE_LIST
的STARTUPINFOEX
來指示應該只繼承兩個流。 該程序具有相同的錯誤行為:
#include <stdio.h>
#include <windows.h>
static counter;
static HANDLE stdout_read;
static void error (char *s)
{
fprintf (stderr,"error: %s (%d)\n",s,GetLastError());
exit (1);
}
static DWORD WINAPI copy_out_func (LPVOID unused)
{
CHAR buffer[512];
for (;;) {
DWORD n_bytes;
if (!ReadFile (stdout_read,buffer,sizeof (buffer),&n_bytes,NULL) || n_bytes==0)
break;
printf ("<%d: ",counter);
WriteFile (GetStdHandle (STD_OUTPUT_HANDLE),buffer,n_bytes,&n_bytes,NULL);
printf (">");
}
return 1;
}
int main(int argc, TCHAR *argv[])
{
SECURITY_ATTRIBUTES sa;
HANDLE stdout_write,copy_out_thread;
char cmd[64]=".\\test.exe";
PROCESS_INFORMATION pi;
SIZE_T size;
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
HANDLE handlesToInherit[3];
STARTUPINFOEX si;
if (argc != 2)
error ("Usage: .\\test.exe N");
counter = argv[1][0]-'0';
if (counter <= 0)
return 0;
printf ("%c",'a'+counter-1);
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!CreatePipe (&stdout_read,&stdout_write,&sa,0))
error ("CreatePipe");
if (!SetHandleInformation (stdout_read,HANDLE_FLAG_INHERIT,0))
error ("SetHandleInformation");
ZeroMemory (&pi,sizeof (PROCESS_INFORMATION));
handlesToInherit[0] = GetStdHandle (STD_INPUT_HANDLE);
handlesToInherit[1] = stdout_write;
handlesToInherit[2] = NULL;
InitializeProcThreadAttributeList (NULL,1,0,&size);
lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)(HeapAlloc (GetProcessHeap(),0,size));
if (lpAttributeList == NULL)
error ("HeapAlloc");
if (!InitializeProcThreadAttributeList (lpAttributeList,1,0,&size))
error ("InitializeProcThreadAttributeList");
if (!UpdateProcThreadAttribute (lpAttributeList,0,PROC_THREAD_ATTRIBUTE_HANDLE_LIST,handlesToInherit,2*sizeof (HANDLE),NULL,NULL))
error ("UpdateProcThreadAttribute");
ZeroMemory (&si,sizeof (STARTUPINFOEX));
si.StartupInfo.cb = sizeof (STARTUPINFOEX);
si.StartupInfo.hStdOutput = stdout_write;
si.StartupInfo.hStdError = stdout_write;
si.StartupInfo.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
si.lpAttributeList = lpAttributeList;
cmd[10]=' ';
cmd[11]='0' + (counter-1);
cmd[12]='\0';
if (!CreateProcess (NULL,cmd,NULL,NULL,TRUE,EXTENDED_STARTUPINFO_PRESENT,NULL,NULL,&si.StartupInfo,&pi))
error ("CreateProcess");
DeleteProcThreadAttributeList (lpAttributeList);
HeapFree (GetProcessHeap(),0,lpAttributeList);
CloseHandle (stdout_write);
copy_out_thread = CreateThread (NULL,0,copy_out_func,NULL,0,NULL);
CloseHandle (pi.hThread);
WaitForSingleObject (pi.hProcess,INFINITE);
WaitForSingleObject (copy_out_thread,INFINITE);
return 0;
}
事實證明,這與混合WriteFile
和printf
。 據我了解,使用printf
打印的文本將被緩沖並在緩沖區滿后傳遞給WriteFile
(或者,在這種情況下,當程序退出時)。 正因為如此,如果你在WriteFile
文件 B 之前printf
A,A 可能實際上出現在B 之后。
因此,解決方案是使用printf
(和朋友)或WriteFile
,或者在切換到 WinAPI 之前插入fflush
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.