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

问题描述 投票:0回答:3

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

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() 并遇到类似的问题。

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

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

上面的知识库文章本质上意味着在 bInheritHandles 设置为 TRUE 的情况下调用 CreateProcess 是很激进的,所以我倾向于修复库 B,使其永远不会这样做。我会这样做:

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

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

c windows winapi
3个回答
7
投票

您可以使用

PROC_THREAD_ATTRIBUTE_HANDLE_LIST
扩展属性来明确指定特定进程继承的处理程序。

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

简短版本:

  • InitializeProcThreadAttributeList() 创建属性列表

  • UpdateProcThreadAttribute 指定要继承的句柄

  • lpAttributeList
    STARTUPINFOEX 中的成员集

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

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


3
投票

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


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.