簡體   English   中英

通過孩子將 CreateProcess 的孫子 output 傳遞給祖父母時的奇怪行為

[英]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 12運行正確,但是對於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_LISTSTARTUPINFOEX來指示應該只繼承兩個流。 該程序具有相同的錯誤行為:

#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;
}

事實證明,這與混合WriteFileprintf 據我了解,使用printf打印的文本將被緩沖並在緩沖區滿后傳遞給WriteFile (或者,在這種情況下,當程序退出時)。 正因為如此,如果你在WriteFile文件 B 之前printf A,A 可能實際上出現B 之后。

因此,解決方案是使用printf (和朋友)或WriteFile ,或者在切換到 WinAPI 之前插入fflush

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM