简体   繁体   中英

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. 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.

2) In Win32 deleting and renaming files can fail when there is more than one handle open to the same file.

3) The Microsoft CRT's open() function will by default create inheritable handles. Additionally the file handles created by default suffer from problem 2 above.

This magic combination creates the following operational problem: Library A calls open() and doesn't expect subsequent renames and deletes to fail. Elsewhere in the process another library B is calling CreateProcess with bInheritHandles set to TRUE (to capture stdin/out/err) temporarily creating duplicate handles. Now occasionally library A's file operations fail. Naturally library A and B are maintained by separate people. I also know of another library A' that uses open() and suffers from a similar problem.

This kb article discusses a related problem and solution. However it still relies on calling CreateProcess with bInheritHandles set to TRUE in the parent process, so it doesn't solve this problem.

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. 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.
  2. Create stdin/out/err pipes and dup the correct ends of those to the intermediate process.
  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.

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? Is rundll + custom entry point a reliable way to setup the intermediate process in step 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.

Raymond Chen's blog post "Programmatically controlling which handles are inherited by new processes in Win32" includes sample code for doing this.

The short version:

  • InitializeProcThreadAttributeList() to create an attribute list

  • UpdateProcThreadAttribute to specify the handles to inherit

  • lpAttributeList member set in STARTUPINFOEX

  • EXTENDED_STARTUPINFO_PRESENT flag set in call to CreateProcess

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? :-)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM