C++ 使用新控制台窗口创建进程,但覆盖某些std i/o句柄

C++ 使用新控制台窗口创建进程,但覆盖某些std i/o句柄,c++,winapi,console,stdio,createprocess,C++,Winapi,Console,Stdio,Createprocess,如果将CreateProcess与标志CREATE_NEW_CONSOLE一起使用,则新进程将其标准输入、输出和错误句柄定向到新控制台窗口。如果要覆盖I/O流,可以通过在STARTUPINFO字段hStdOutput、hStdInput和hStdError中设置句柄并设置STARTF_USESTDHANDLES标志来实现 但是如果您只想覆盖其中一个句柄,该怎么办?例如,我可能希望将stderr重定向到一个文件,同时将stdout和stdin连接到新的控制台窗口 STARTF_USESTDHAND

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

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

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

我尝试使用NULL,它似乎在Windows8.1上工作,但在Windows7上不工作


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

如果我解释正确,您应该使用
GetStdHandle(STD\u INPUT\u HANDLE)
GetStdHandle(STD\u OUTPUT\u HANDLE)
用于其他两个句柄。

根据本MSDN支持文章:

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

hStdInput = GetStdHandle(STD_INPUT_HANDLE);
根据报告:

标准输入句柄
(德沃德)-10
标准输入设备最初,这是控制台输入缓冲区,CONIN$

标准输出句柄
(德沃德)-11
标准输出设备最初,这是活动的控制台屏幕缓冲区,CONOUT$

标准错误句柄
(德沃德)-12
标准误差装置最初,这是活动的控制台屏幕缓冲区,CONOUT$

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

附加/分离行为

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

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

当父级同时使用CREATE\u NEW\u CONSOLE和STARTF\u USESTDHANDLES创建控制台进程时,除非标准句柄的现有值为NULL或控制台伪句柄,否则不会替换标准句柄。

因此,如果父进程的STDIN没有被重定向,
GetStdHandle(STD\u INPUT\u HANDLE)
将返回NULL或引用
CONIN$
的伪句柄。当通过
STARTUPINFO
将该值传递给子进程时,子进程将收到一个STDIN的控制台句柄,该句柄位于运行它的任何控制台中。另一方面,如果父进程的STDIN已被重定向,
GetStdHandle(STD\u INPUT\u HANDLE)
将返回一个实际句柄到子进程将继承和访问的实际文件/管道/etc

这同样适用于STDOUT和STDERR


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

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

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

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

  • 因此,如果您是一个控制台进程,并且启动时没有重定向输出,那么您的标准输出句柄是7。如果打开CONOUT$的另一个句柄,它将具有不同的值。如果关闭句柄7,然后打开CONOUT$的句柄,您将再次得到7

  • 当启动控制台进程时,子进程中通常会出现一个CONOUT$的句柄,该句柄的神奇值为7(不考虑重定向)。如果您使用了
    STARTF\u USESTDHANDLES
    ,则当且仅当您指定7作为标准输出句柄时,子级的标准输出才会转到控制台。如果您为CONOUT$指定一个具有任何其他值的句柄,则该句柄将不起作用

  • 有时,在标准输出重定向的情况下启动控制台进程时,子进程中没有CONOUT$的句柄。特别是,当
    cmd.exe
    启动c