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

Mar*_*ren 6 c windows winapi

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

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

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

3) Microsoft CRT 的 open() 函数默认会创建可继承的句柄。此外,默认情况下创建的文件句柄会遇到上述问题 2。

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

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

我想知道其他人是否遇到过这个问题,是否没有众所周知的解决方案?

上面的 kb 文章基本上暗示在将 bInheritHandles 设置为 TRUE 的情况下调用 CreateProcess 是不明智的,所以我倾向于修复库 B,使其永远不会这样做。我会这样做:

  1. 创建一个挂起的中间进程(最好使用 rundll 运行库 B 中的自定义入口点),并将 bInheritHandles 设置为 FALSE。
  2. 创建 stdin/out/err 管道并将这些管道的正确末端复制到中间进程。
  3. 以某种方式将被欺骗的句柄传递给中间进程。
  4. 恢复中间过程。
  5. 从中间进程用来自父进程的管道填写 STARTUPINFO 并在 bInheritHandles 设置为 TRUE 的情况下调用 CreateProcess。

这是一个好的策略还是有更好的解决方案?您建议如何将被欺骗的句柄传递给步骤 3 中的中间过程?rundll + 自定义入口点是在步骤 1 中设置中间过程的可靠方法吗?

Har*_*ton 5

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

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

简短版本:

  • InitializeProcThreadAttributeList() 创建属性列表

  • UpdateProcThreadAttribute 指定要继承的句柄

  • lpAttributeList STARTUPINFOEX 中的成员集

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

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


Rem*_*eau 4

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