简体   繁体   English

在Windows中,如何创建子进程并捕获其stdin,stdout和stderr,而无需复制任何可继承的句柄?

[英]In Windows, how can one create a child process and capture its stdin, stdout, and stderr, without duplicating any inheritable handles?

There are at least three parts to this problem, so bear with me: 这个问题至少有三个部分,所以请耐心等待:

1) CreateProcess has a parameter bInheritHandles, that causes the child process to inherit all of the inheritable handles in the parent process. 1)CreateProcess有一个参数bInheritHandles,它使子进程继承父进程中的所有可继承句柄。 This option must be set to TRUE to allow the parent to specify stdin, stdout, and stderr handles for the child in the STARTUPINFO parameter. 必须将此选项设置为TRUE,以允许父级在STARTUPINFO参数中为子级指定stdin,stdout和stderr句柄。

2) In Win32 deleting and renaming files can fail when there is more than one handle open to the same file. 2)在Win32中,当同一个文件有多个句柄打开时,删除和重命名文件可能会失败。

3) The Microsoft CRT's open() function will by default create inheritable handles. 3)Microsoft CRT的open()函数默认创建可继承的句柄。 Additionally the file handles created by default suffer from problem 2 above. 此外,默认情况下创建的文件句柄会遇到上面的问题2。

This magic combination creates the following operational problem: Library A calls open() and doesn't expect subsequent renames and deletes to fail. 这种神奇的组合会产生以下操作问题:库A调用open()并且不希望后续的重命名和删除失败。 Elsewhere in the process another library B is calling CreateProcess with bInheritHandles set to TRUE (to capture stdin/out/err) temporarily creating duplicate handles. 在此过程中的另一个库B调用CreateProcess,并将bInheritHandles设置为TRUE(以捕获stdin / out / err),暂时创建重复句柄。 Now occasionally library A's file operations fail. 现在偶尔库A的文件操作失败了。 Naturally library A and B are maintained by separate people. 自然地,图书馆A和B由不同的人维护。 I also know of another library A' that uses open() and suffers from a similar problem. 我也知道另一个使用open()并且遇到类似问题的库A'。

This kb article discusses a related problem and solution. 这篇kb文章讨论了一个相关的问题和解决方案。 However it still relies on calling CreateProcess with bInheritHandles set to TRUE in the parent process, so it doesn't solve this problem. 但是它仍然依赖于在父进程中将bInheritHandles设置为TRUE来调用CreateProcess,因此它无法解决此问题。

I am wondering if others have hit this problem and if there isn't a well known solution? 我想知道其他人是否遇到过这个问题,是否有一个众所周知的解决方案?

The kb article above essentially implies that calling CreateProcess with bInheritHandles set to TRUE is racy, so my inclination is to fix library B such that it never does that. 上面的kb文章基本上暗示调用带有bInheritHandles设置为TRUE的CreateProcess很生,所以我倾向于修复库B,使它永远不会这样做。 I would do this by: 我会这样做:

  1. Create a suspended intermediate process (ideally by using rundll to run a custom entry point in library B) with bInheritHandles set to FALSE. 创建一个暂停的中间过程(理想情况下,使用rundll在库B中运行自定义入口点),并将bInheritHandles设置为FALSE。
  2. Create stdin/out/err pipes and dup the correct ends of those to the intermediate process. 创建stdin / out / err管道并将这些管道的正确结尾复制到中间过程。
  3. Pass the duped handles to the intermediate process somehow. 以某种方式将欺骗手柄传递给中间过程。
  4. Resume the intermediate process. 恢复中间过程。
  5. From the intermediate process fill out the STARTUPINFO with the pipes from the parent and call CreateProcess with bInheritHandles set to TRUE. 在中间过程中,使用父项中的管道填充STARTUPINFO,并将bInheritHandles设置为TRUE调用CreateProcess。

Is this a good strategy or is there some better solution? 这是一个好策略还是有更好的解决方案? How would you recommend passing the duped handles to the intermediate process in step 3? 您如何建议在步骤3中将欺骗手柄传递给中间过程? Is rundll + custom entry point a reliable way to setup the intermediate process in step 1? rundll +自定义入口点是在步骤1中设置中间过程的可靠方法吗?

如果您有权访问实际的文件句柄,则可以在调用CreateProcess()之前使用SetHandleInformation()删除HANDLE_FLAG_INHERIT标志。

You can use the PROC_THREAD_ATTRIBUTE_HANDLE_LIST extended attribute to explicitly specify exactly which handles a particular process inherits. 您可以使用PROC_THREAD_ATTRIBUTE_HANDLE_LIST扩展属性显式指定特定进程继承的句柄。

Raymond Chen's blog post "Programmatically controlling which handles are inherited by new processes in Win32" includes sample code for doing this. Raymond Chen的博客文章“以编程方式控制哪些句柄由Win32中的新进程继承”包括用于执行此操作的示例代码。

The short version: 简短版本:

  • InitializeProcThreadAttributeList() to create an attribute list InitializeProcThreadAttributeList()创建属性列表

  • UpdateProcThreadAttribute to specify the handles to inherit UpdateProcThreadAttribute指定要继承的句柄

  • lpAttributeList member set in STARTUPINFOEX lpAttributeList成员在STARTUPINFOEX中设置

  • EXTENDED_STARTUPINFO_PRESENT flag set in call to CreateProcess 在调用CreateProcess时设置了EXTENDED_STARTUPINFO_PRESENT标志

Requires Windows Vista, so might not have solved the OPs problem when this question was originally asked, but everyone's using Vista or later by now, right? 需要Windows Vista,因此在最初询问此问题时可能没有解决OP问题,但是现在每个人都在使用Vista或更高版本,对吧? :-) :-)

您可以使用ZwQuerySystemInformation(SystemHandleInformation,...)ntdll.dll函数查找您的进程拥有的所有句柄,然后按照Remy的建议在每个句柄上查找所有SetHandleInformation,以删除HANDLE_FLAG_INHERIT标志。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何创建以 FILE* 作为其标准输入、标准输出或标准错误的子进程? - How to create create subprocess with a FILE* as its stdin, stdout or stderr? 如何在 Windows 上的 CreateProcess() 中不继承标准输入、标准输出和标准错误 - How to NOT inherit stdin, stdout and stderr in CreateProcess() on Windows 如何将一个子进程中的stdout链接到C中另一个子进程中的stdin? - How do I chain stdout in one child process to stdin in another child in C? 是否每个进程都将其stdin stdout stderr定义为Keyboard,Terminal等? - Does every process have its stdin stdout stderr defined as Keyboard, Terminal etc? 子进程如何从管道读取stdout,而父进程如何将stdin写入管道? - How can the child process read stdout from the pipe and the parent process write stdin to the pipe? 与子进程stdout / stdin通信 - Communicate with child process stdout/stdin 可以假设`stdin`,`stdout`和`stderr`的值是常量吗? - Can the values of `stdin`, `stdout`, and `stderr` be assumed to be constant? 如何将子进程的标准输出(stderr)重定向到winapi中的套接字? - How redirect stdout (stderr) of child process to socket in winapi? 混淆stdin,stdout和stderr的工作方式 - Confused how stdin, stdout and stderr work 如何控制popen stdin,stdout,stderr重定向? - how to control popen stdin, stdout, stderr redirection?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM