windows 使 CreateProcess 继承调用进程的控制台

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/340356/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 11:42:20  来源:igfitidea点击:

Making CreateProcess inherit the console of the calling process

windowsconsolecreateprocess

提问by

When I call CreateProcess in Windows, the new process doesn't seem to inherit the console of the calling process. I made a test program that runs "ruby xtest", xtest being a script that writes "hello" to standard output. I ran this test program from Emacs, and get no output. I also tried the following code calling GetStdHandle, but again, no output. Then I tried passing CREATE_NEW_CONSOLE in dwCreationFlags to CreateProcess, which made a whole new window with the Ruby output. Finally, I made a simple fork/exec test program and compiled it using Cygwin's GCC. This program worked: the Ruby output showed up in Emacs as expected. I tried to decipher the Cygwin source code in http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/spawn.cc?rev=1.268&content-type=text/x-cvsweb-markup&cvsroot=srcbut failed. So, how do you make the new process inherit the console of the parent process such that the output from the child shows up as expected?

当我在 Windows 中调用 CreateProcess 时,新进程似乎没有继承调用进程的控制台。我制作了一个运行“ruby xtest”的测试程序,xtest 是一个将“hello”写入标准输出的脚本。我从 Emacs 运行了这个测试程序,但没有得到任何输出。我还尝试了以下调用 GetStdHandle 的代码,但同样没有输出。然后我尝试将 dwCreationFlags 中的 CREATE_NEW_CONSOLE 传递给 CreateProcess,它使用 Ruby 输出创建了一个全新的窗口。最后,我做了一个简单的 fork/exec 测试程序,并使用 Cygwin 的 GCC 编译它。该程序有效:Ruby 输出按预期显示在 Emacs 中。我试图破译http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/spawn.cc?rev=1.268&content-type=text/x-cvsweb-markup& 中的 Cygwin 源代码但失败了。那么,如何让新进程继承父进程的控制台,以便子进程的输出按预期显示?

STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if(!CreateProcess(0, "ruby xtest", 0, 0, 1, 0, 0, 0, &si, &pi)) die("CreateProcess");

回答by Steffen

I know, this thread is rather old, however, I just ran into the same problem.

我知道,这个线程已经很老了,但是,我遇到了同样的问题。

Just as for the TS, the console handle was inherited and working fine under Cygwin, but not on a Windows console. Instead, the output on stdout was neither shown, nor any error was reported. Inherited Pipe handles worked still fine.

就像 TS 一样,控制台句柄在 Cygwin 下是继承的并且可以正常工作,但在 Windows 控制台上则不然。相反,stdout 上的输出既没有显示,也没有报告任何错误。继承的管道句柄仍然可以正常工作。

I took me some time to identify the (now obvious) problem: CreateProcess() was called with CREATE_NO_WINDOW. Dropping this flag, console output is fine. (Though, according to the code of the TS, they never set this flag in the first place.)

我花了一些时间来确定(现在很明显)问题:CreateProcess() 是用 CREATE_NO_WINDOW 调用的。删除此标志,控制台输出很好。(不过,根据 TS 的代码,他们从一开始就没有设置这个标志。)

Hope this might be helpful for people who also stumble across this thread, like myself.

希望这可能对像我这样偶然发现此线程的人有所帮助。

回答by Thomas Munk

According to Microsoft documentation, lpCommandLine(2. parameter):

根据 Microsoft 文档,lpCommandLine(2. 参数):

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

此函数的 Unicode 版本 CreateProcessW 可以修改此字符串的内容。因此,该参数不能是指向只读内存的指针(例如 const 变量或文字字符串)。如果此参数是一个常量字符串,该函数可能会导致访问冲突。

When I stopped using a constant here it worked for me. I didn't need the STARTF_USESTDHANDLES and GetStdHandle thing.

当我在这里停止使用常量时,它对我有用。我不需要 STARTF_USESTDHANDLES 和 GetStdHandle 东西。

This code from a console prg runs and outputs another console exe in the same console:

控制台 prg 中的此代码运行并在同一控制台中输出另一个控制台 exe:

FillChar(SI, SizeOf(SI), 0);
SI.cb:=SizeOf(SI);
FillChar(PI, SizeOf(PI), 0);
if CreateProcess(nil, CmdLineVar, nil, nil, False, 0, nil, nil, SI, PI) then ...

回答by MSN

I've done this by passing in pipes for hStdInput, hStdOutput, and hStdErrorand manually routing data from the hStdOutputand hStdErrorpipes to the console.

我已经通过管道传递的做到了这一点hStdInputhStdOutputhStdError手动路由从数据hStdOutputhStdError管道到控制台。

回答by MSN

Not sure if debeige ever solved this, but I needed the same thing, but starting up another thread to listen to stdout output, just to put it on stdout seemed nuts to me.

不确定 debeige 是否解决了这个问题,但我需要同样的东西,但是启动另一个线程来监听 stdout 输出,只是把它放在 stdout 上对我来说似乎很疯狂。

The following works for me, and is slightly different than what he originally posted. I thought at first it wouldn't work if you don't set si.cb, but when I commented that in mine, it still worked, so... YMMV.

以下内容对我有用,与他最初发布的内容略有不同。我一开始以为如果你不设置 si.cb 就行不通,但是当我在我的评论中评论它时,它仍然有效,所以...... YMMV。

   STARTUPINFO siStartInfo;
   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
   siStartInfo.cb = sizeof(STARTUPINFO); 
   siStartInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE); 
   siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); 
   siStartInfo.hStdInput = g_hChildStd_IN_Rd;  // my outgoing pipe
   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

// Create the child process. 

   bSuccess = CreateProcess(
      NULL,         
      szCmdline,    
      NULL,         
      NULL,         
      TRUE,         
      0,            
      NULL,         
      NULL,         
      &siStartInfo, 
      &piProcInfo);