首页 文章

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

提问于
浏览
4

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

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由不同的人维护 . 我也知道另一个使用open()的库A'并且遇到类似的问题 .

这个kb article讨论了一个相关的问题和解决方案 . 但是它仍然依赖于在父进程中将bInheritHandles设置为TRUE调用CreateProcess,因此它无法解决此问题 .

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

上面的kb文章基本上暗示调用带有bInheritHandles设置为TRUE的CreateProcess是非常的,所以我倾向于修复库B,使它永远不会这样做 . 我会这样做:

  • 创建一个暂停的中间过程(理想情况下,通过使用rundll在库B中运行自定义入口点),并将bInheritHandles设置为FALSE .

  • 创建stdin / out / err管道并将这些管道的正确结尾复制到中间过程 .

  • 以某种方式将欺骗手柄传递给中间过程 .

  • 恢复中间过程 .

  • 在中间过程中,使用父项中的管道填充STARTUPINFO,并将bInheritHandles设置为TRUE调用CreateProcess .

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

3 回答

  • 0

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

  • 1

    您可以使用 PROC_THREAD_ATTRIBUTE_HANDLE_LIST extended属性显式指定特定进程继承的句柄 .

    Raymond Chen的博客文章"Programmatically controlling which handles are inherited by new processes in Win32"包含了执行此操作的示例代码 .

    简短版本:

    • InitializeProcThreadAttributeList()创建属性列表

    • UpdateProcThreadAttribute指定要继承的句柄

    • lpAttributeList 成员在STARTUPINFOEX中设置

    • EXTENDED_STARTUPINFO_PRESENT标志在调用CreateProcess时设置

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

  • 1

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

相关问题