我正在開發一個應用程式 A,它呼叫CreateProcess運行子行程 B,而子行程 B 又呼叫createProcess運行子行程 C。C 的輸出應該由 B 傳遞,然后由 A 讀取。當我自己運行 B 時(外部A),它作業正常,C的輸出顯示在控制臺上。但是當我運行 A 時,它會看到 B 的所有輸出,除了從 C 傳遞的內容。顯然,我正在為孫子或類似的東西打一個特殊的案例。
為了進行實驗,我創建了一個簡單的示例test.c,它以一個數字作為引數。它輸出對應于數字的字母(a對于 1 等)。然后它使用 遞回啟動相同的程式CreateProcess,但減少引數。它以格式傳遞來自子級的所有輸出<N: OUTPUT>,其中N是數字引數。
我希望看到以下結果:
> .\test.exe 1
a
> .\test.exe 2
b<2: a>
> .\test.exe 3
c<3: b><3: <2: a>>
我意識到由于緩沖最后一個輸出也可能是這樣c<3: b<2: a>>的——重要的是輸出a嵌套在一個<2:>塊中,并且該<2:>塊嵌套在一個<3:>塊中:這表明孫子的輸出首先被接收由孩子傳給家長。
我的程式對引數1和的行為正確2,但3我始終得到以下輸出:
c<3: a><3: b<2: >>
這對我來說根本沒有意義。就好像孫子的輸出直接由最頂層的行程接收,因為我們得到<3: a>. 而且我也不知道怎么解釋<3: b<2: >>。
如何修復此示例,以便孫子的輸出首先被孩子看到,然后傳遞給父母?
#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;
}
我認為這可能是因為祖父母的某些句柄被孫子錯誤地繼承了。因此,我創建了一個更長的示例,使用STARTUPINFOEXwith aLPPROC_THREAD_ATTRIBUTE_LIST來指示應該只繼承兩個流。該程式具有相同的錯誤行為:
#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;
}
uj5u.com熱心網友回復:
事實證明,這與混合WriteFile和printf. 據我了解,一旦緩沖區已滿(或者,在這種情況下,程式退出時) ,列印的文本printf將被緩沖并傳遞給。WriteFile正因為如此,如果你printfA 在你WriteFileB 之前,A 實際上可能出現在B之后。
因此,解決方案是使用printf(&friends) 或WriteFile始終使用,或者fflush在切換到 WinAPI 之前插入。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/419795.html
標籤:
上一篇:什么是檔案夾的lpstr過濾器?
