通过 child 将 CreateProcess 的 grandchild 输出传递给祖父母时的奇怪行为
Weird behaviour when passing grandchild output of CreateProcess to the grandparent through the child
我正在开发一个应用程序 A,它调用 CreateProcess
到 运行 一个 child 进程 B,后者又调用 createProcess
到 运行 一个child 处理 C。C 的输出应由 B 传递,然后由 A 读取。当我 运行 B 自己(在 A 之外)时,它工作正常,C 的输出显示在控制台。但是当我 运行 A 时,它可以看到 B 的所有输出 除了从 C 传递的内容。 Apparently,我正在为 grandchildren 或类似的东西打一个特例。
为了进行实验,我创建了一个简单的示例 test.c
,它以数字作为参数。它输出与数字对应的字母(a
表示 1,等等)。然后它使用 CreateProcess
递归地启动同一个程序,但减少参数。它以 <N: OUTPUT>
格式传递来自 child 的所有输出,其中 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:>
块中:这表明 grandchild 的输出首先由 child 接收,然后传递给 parent.
我的程序对于参数 1
和 2
的行为是正确的,但是对于 3
我始终得到这个输出:
c<3: a><3: b<2: >>
这对我来说完全没有意义。就好像grandchild的输出直接被最顶层的进程接收了一样,因为我们得到了<3: a>
。我也不知道如何解释 <3: b<2: >>
。
如何修复此示例,以便 child 首先看到 grandchild 的输出,然后传递给 parent?
#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]='[=13=]';
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;
}
我认为这可能是因为 grandparent 的某些句柄被 grandchild 错误地继承了。因此,我使用 STARTUPINFOEX
和 LPPROC_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]='[=14=]';
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
。因此,如果你 printf
A 在你 WriteFile
B 之前,A 可能实际上出现 在 B.
之后
因此,解决方案是始终使用 printf
(和朋友)或 WriteFile
,或者在切换到 WinAPI 之前插入 fflush
。
我正在开发一个应用程序 A,它调用 CreateProcess
到 运行 一个 child 进程 B,后者又调用 createProcess
到 运行 一个child 处理 C。C 的输出应由 B 传递,然后由 A 读取。当我 运行 B 自己(在 A 之外)时,它工作正常,C 的输出显示在控制台。但是当我 运行 A 时,它可以看到 B 的所有输出 除了从 C 传递的内容。 Apparently,我正在为 grandchildren 或类似的东西打一个特例。
为了进行实验,我创建了一个简单的示例 test.c
,它以数字作为参数。它输出与数字对应的字母(a
表示 1,等等)。然后它使用 CreateProcess
递归地启动同一个程序,但减少参数。它以 <N: OUTPUT>
格式传递来自 child 的所有输出,其中 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:>
块中:这表明 grandchild 的输出首先由 child 接收,然后传递给 parent.
我的程序对于参数 1
和 2
的行为是正确的,但是对于 3
我始终得到这个输出:
c<3: a><3: b<2: >>
这对我来说完全没有意义。就好像grandchild的输出直接被最顶层的进程接收了一样,因为我们得到了<3: a>
。我也不知道如何解释 <3: b<2: >>
。
如何修复此示例,以便 child 首先看到 grandchild 的输出,然后传递给 parent?
#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]='[=13=]';
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;
}
我认为这可能是因为 grandparent 的某些句柄被 grandchild 错误地继承了。因此,我使用 STARTUPINFOEX
和 LPPROC_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]='[=14=]';
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
。因此,如果你 printf
A 在你 WriteFile
B 之前,A 可能实际上出现 在 B.
因此,解决方案是始终使用 printf
(和朋友)或 WriteFile
,或者在切换到 WinAPI 之前插入 fflush
。