具有新控制台窗口的CreateProcess,但覆盖一些标准i / o句柄

时间:2015-05-27 23:42:55

标签: c++ winapi console stdio createprocess

如果将CreateProcess与标志CREATE_NEW_CONSOLE一起使用,则新进程将其标准输入,输出和错误句柄定向到新控制台窗口。如果要覆盖I / O流,可以通过设置STARTUPINFO字段hStdOutput,hStdInput和hStdError中的句柄并设置标志STARTF_USESTDHANDLES来实现。

但是如果你只想覆盖其中一个句柄呢?例如,我可能希望将stderr重定向到文件,同时将stdout和stdin连接到新的控制台窗口。

STARTF_USESTDHANDLES标志告诉CreateProcess替换所有句柄,而不是将它们连接到新控制台窗口的句柄。所以看来我们必须提供所有三个句柄。显然我可以将hStdError设置为日志文件的句柄,但是hStdInput和hStdOutput应该使用什么值?

我尝试使用NULL,这似乎适用于Windows 8.1,但它在Windows 7上不起作用。

我还考虑先创建一个控制台窗口,然后使用句柄调用CreateProcess到新控制台窗口的缓冲区(并省略CREATE_NEW_CONSOLE标志)。不幸的是,父进程也是一个控制台应用程序,似乎控制台应用程序无法创建第二个控制台窗口。

3 个答案:

答案 0 :(得分:4)

根据此MSDN支持文章:

How to spawn console processes with redirected standard handles

  

如果父进程只希望重定向一个或两个标准句柄,则为特定句柄指定GetStdHandle()会导致子进程创建标准句柄,而不需要重定向。例如,如果父进程只需要重定向子进程的标准输出和错误,那么STARTUPINFO结构的hStdInput成员将按如下方式填充:

hStdInput = GetStdHandle(STD_INPUT_HANDLE);

根据GetStdHandle() documentation

  

STD_INPUT_HANDLE
  (DWORD)-10
  标准输入设备。 最初,这是控制台输入缓冲区,CONIN $

     

STD_OUTPUT_HANDLE
  (DWORD)-11
  标准输出设备。 最初,这是主动控制台屏幕缓冲区,CONOUT $

     

STD_ERROR_HANDLE
  (DWORD)-12
  标准错误设备。 最初,这是主动控制台屏幕缓冲区,CONOUT $

     

...

     

可以通过调用SetStdHandle来重定向进程的标准句柄,在这种情况下,GetStdHandle返回重定向的句柄。如果已重定向标准句柄,则可以在调用CreateFile函数时指定CONIN $值以获取控制台输入缓冲区的句柄。同样,您可以指定CONOUT $值以获取控制台的活动屏幕缓冲区的句柄。

     

附加/分离行为

     

连接到新控制台时,除非在创建流程期间指定了STARTF_USESTDHANDLES,否则标准句柄始终替换为控制台句柄。

     

如果标准句柄的现有值为NULL,或者标准句柄的现有值看起来像控制台伪句柄,则句柄将替换为控制台句柄。

     

当父级使用CREATE_NEW_CONSOLE和STARTF_USESTDHANDLES创建控制台进程时,除非标准句柄的现有值为NULL或控制台伪句柄,否则不会替换标准句柄。

因此,如果未重定向父进程的STDIN,GetStdHandle(STD_INPUT_HANDLE)将返回NULL或引用CONIN$的伪句柄。当该值通过STARTUPINFO传递给子进程时,子进程将收到它正好运行的控制台的STDIN控制台句柄。另一方面,如果父进程的STDIN已被重定向,GetStdHandle(STD_INPUT_HANDLE)将返回真实文件/管道/等的实际句柄,孩子将继承并访问。

这同样适用于STDOUT和STDERR。

因此,如果要重定向子项的STDIN / OUT / ERR句柄,则必须将hStdInput/Output/Error设置为自己的句柄。如果您希望孩子接收默认句柄,请使用GetStdHandle()并让CreateProcess()根据父母本身是否被重定向来决定孩子接收的句柄类型。

答案 1 :(得分:2)

至少在Windows 7上,documentation here(由现有答案引用)具有误导性。建议的方法仅在父进程没有重定向输入时才有效。

这是我的机器上观察到的实际行为:

  • 当进程打开CONOUT $的句柄时(例如),返回的句柄总是具有相同的数值(在我的机器上,CONOUT $的句柄总是7),除非该句柄已存在。

  • 因此,如果您是一个控制台进程,并且未使用重定向输出启动,则标准输出句柄为7.如果打开另一个CONOUT $句柄,它将具有不同的值。如果您关闭手柄7,然后打开CONOUT $的手柄,您将再次获得7。

  • 启动控制台进程时,子进程中通常会出现一个带有魔术值7的CONOUT $句柄(无论重定向如何)。如果您已使用STARTF_USESTDHANDLES,则当且仅当您将7指定为标准输出句柄时,子项的标准输出才会转到控制台。如果指定CONOUT $的句柄具有任何其他值,则它将不起作用。

  • 有时,当启动控制台进程并重定向标准输出时,子进程中不存在CONOUT $句柄。特别是,当cmd.exe启动带有重定向标准输出的控制台进程时,会发生这种情况。到目前为止,我一直无法弄清楚要与CreateProcess一起使用哪些参数组合来实现这一目标。

  • CONIN $的行为类似;在我的机器上,CONIN $的神奇值是3.(我没有调查标准错误是如何工作的。)

结果是我不相信使用这种行为是安全的,除非您完全控制父进程的启动方式,因为除非标准句柄已指向控制台,否则没有可靠的方法使用正确的魔术值获取控制台的句柄。

相反,使用CREATE_NEW_CONSOLE启动代理流程,而不使用 STARTF_USESTDHANDLES。从那个进程开始,既然您知道它是如何启动的,那么您就知道标准句柄将具有正确的魔术值,因此将它们指定为子进程的句柄是安全的。

答案 2 :(得分:1)

如果我正确解释this documentation,您应该使用GetStdHandle(STD_INPUT_HANDLE)GetStdHandle(STD_OUTPUT_HANDLE)作为其他两个句柄。