在Windows中,如何创buildsubprocess并捕获其stdin,stdout和stderr,而不复制任何可inheritance的句柄?

这个问题至less有三个部分,所以请耐心等待:

1)CreateProcess有一个参数bInheritHandles,它使subprocessinheritance父进程中的所有可inheritance的句柄。 此选项必须设置为TRUE以允许父级为STARTUPINFO参数中的子级指定stdin,stdout和stderr句柄。

2)在Win32中,当有多个句柄对同一文件打开时,删除和重命名文件可能会失败。

3)Microsoft CRT的open()函数默认会创build可inheritance的句柄。 此外,默认情况下创build的文件句柄受到上述问题2的困扰。

这个神奇的组合创build了以下操作问题:库A调用open(),并且不希望后续的重命名和删除失败。 在另一个过程中,另一个库B正在调用CreateProcess,将bInheritHandles设置为TRUE(捕获stdin / out / err),临时创build重复句柄。 现在偶尔库A的文件操作失败。 自然,图书馆A和B由不同的人维护。 我也知道另一个使用open()的库A',并遭受类似的问题。

这篇文章讨论了一个相关的问题和解决scheme。 但是,它仍然依赖于在父进程中将bInheritHandles设置为TRUE来调用CreateProcess,所以它不能解决这个问题。

我想知道如果别人有这个问题,如果没有一个众所周知的解决scheme?

上面的kb文章基本上意味着调用CreateProcess与bInheritHandles设置为TRUE是活泼的,所以我的意图是修复库B,使它从来没有这样做。 我会这样做:

  1. 创build一个暂停的中间过程(理想情况下,通过使用rundll在库B中运行自定义入口点),并将bInheritHandles设置为FALSE。
  2. 创buildstdin / out / errpipe道,并将这些pipe道的正确结尾复制到中间进程。
  3. 以某种方式将被欺骗的手柄传递给中间过程。
  4. 恢复中间过程。
  5. 从中间过程填充STARTUPINFO与来自父级的pipe道,并调用CreateProcess与bInheritHandles设置为TRUE。

这是一个好策略还是有一些更好的解决scheme? 你会如何推荐将步骤手柄交给步骤3中的中间过程? Rundll +自定义入口点是在步骤1中设置中间过程的可靠方法吗?

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

您可以使用PROC_THREAD_ATTRIBUTE_HANDLE_LIST扩展属性明确指定特定进程继承的处理。

Raymond Chen的博客文章“以编程方式控制Win32中的新进程继承哪些句柄”包含用于执行此操作的示例代码。

简短版本:

  • InitializeProcThreadAttributeList()创建一个属性列表

  • UpdateProcThreadAttribute指定要继承的句柄

  • 在STARTUPINFOEX中设置lpAttributeList成员

  • 在调用CreateProcess时设置了EXTENDED_STARTUPINFO_PRESENT标志

需要Windows Vista,所以当这个问题最初被问到时可能没有解决OP问题,但现在每个人都使用Vista或更高版本,对吧? 🙂

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