重定向子进程控制台程序的输入输出

重定向子进程控制台程序的输入输出

重定向所举行的工作还于大进程,但需要子进程遵守下面的规则:

分层进程程序于出口代码后,等待输入之前要调用fflush(stdout)函数,这样把出口的情节放入缓冲区,父进程才能够马上的朗诵到输出数据。

切莫守上述规则就是从来不道落实有效的相互了,cmd.exe是遵循这规则的样子,大部分控制台程序都未信守这个规则。今天本人打算让Google的V8
Javascript
的Shell搞一个GUI,方便我输入Javascript程序,就撞了v8_shell不守这个的题材。好于产生源代码,研究了转,加上两只fflush(stdout)就实行了。

再有一个内需注意的就是,在向子进程的stdin写内容常常,一定要以终极加上”\r\n”,否则直到父进程终止时子进程才会收到这些输入,这时既尽晚矣。这挺意外,但实在这样。加上fflush(stdin)应该会愈保险。

MSDN的CreatePipe函数的Examples的链接内容对这主题也起描述,但不够清晰扼要。
CodeProject也来只例证,能够科学运行,但建议您用cmd.exe做子进程展开考。

管道是千篇一律种出个别单端点的通信通道。你以管道在少独过程中还是平等进程内交换数据。它小像手提对讲机,双方同样人一个就算足以打电话了。
留存个别栽管道:匿名管道和命名管道。匿名管道是匿名的,使用时若随便需了解她的名。命名管道相反,使用时若要了解它的名。
外一样种植分类:单为管道和双向管道。单为管道数据流式单为的,像发传真;双向管道数据流是双向的,像打电话。
匿名管道连接独自为的,命名管道可以是才为或双向的。命名管道可以为此当网络环境,服务器通过它连接至几只客户。在准读本,我们详细研究匿名管道。匿名管道用于父子进程中的通信,或子进程之中的通信。当你处理控制台程序时,匿名管道真的很有因此。控制台程序类似于DOS窗口,但是其是一心的32号Windows程序,可以以任何GUI函数,只不过它比常见的GUI程序多了一个控制台而已。控制台程序来三单句子柄,标准输入、标准输出、错误输出。
君可调用GetStdHandle获得控制台程序的老三独词柄。GUI程序没有控制台,如果你调用GetStdHandle,会回来错误。如果你想当GUI程序中采用控制台,那么可使用AllocConsole,用完后恳求调用FreeConsole。
匿名管道经常用于重定向控制台子进程的输入输入,父进程可以是控制台或GUI程序。我们得轮换控制台程序的输入和出口,而雅控制台程序浑然不觉。引用面向对象编程的术语,这是一样种植多态。这种措施充分强劲,我们不用对进程举行任何移。
除此以外你要是明了的凡控制台程序于乌得到那些专业句柄。当控制高过程被创造时,父进程有少个选项:为子进程创造一个新的控制台,或者让子进程继续它们的控制台。
介绍一下CreatePipe函数,该函数用于创造匿名管道。
BOOL WINAPI CreatePipe(
  __out     PHANDLE hReadPipe,
  __out     PHANDLE hWritePipe,
  __in_opt  LPSECURITY_ATTRIBUTES lpPipeAttributes,
  __in      DWORD nSize
);
hReadPipe 接受管道读句柄的变量指针
hWritePipe 接受管道写句柄的变量指针
lpPipeAttributes 安全性能,决定子进程是否可继续返回的宣读写句柄
nSize  管道保留下的休息冲大小,这仅仅是一个建议大小,你得用NULL表示以缺省大小。
如调用成功,返回值非0,否则,返回值为0。
苟调用成功,你晤面获得两独句子柄,一头用于读,一头用于形容。下面我列出重定向子进程正式输出到父亲进程的手续,我们下GUI做吧父进程,因为及时更有意义。

1
使用CreatePipe创建一个匿名管道。不要忘记设置SECURITY_ATTRIBUTES的bInheritable成员为TRUE,这样子进程就得延续这些词柄。
2
调用CreateProcess创建子进程。我们用准备CreateProcess函数的STARTUPINFO参数
      cb : STARTUPINFO结构体的高低。
      dwFlags :
flag枚举。为了达成目的,我们应有采取STARTF_USESHOWWINDOW |
STARTF_USESTDHANDLES
      hStdOutput and hStdError :
你愿意子进程使的业内输出及错误输出的句柄。为了上目的,我们管这个管道的写端用于子进程的正统输出以及错误输出。这样当子进程输出错误与外信息时,实际是在朝着管道写入。
      wShowWindow
决定窗口的展示状态。为了达到目的,我们运用SW_HIDE来隐藏控制高窗口。
 子进程创造后,是居于休眠状态的。
3
使用CloseHandle关闭管道的抒写句柄。这是得的。父进程不以这句柄,如果在多独写句柄,那么管道不见面工作。但是,不要在调用CreateProcess前关闭管道的勾勒句柄,否则管道就作废了。你该在CreateProcess调用返回后,从读管道端读取子进程的数据之前关闭管道。
4
使用ReadFile从读管道端读取数据。在公调用ReadFile后,子进程会开始运行。你得管读数据想象为用吸管喝饮料。你直接利用ReadFile读数据,直到其回到回0。对于读来之数码,你想怎样就什么,比如加大上一个Edit控件。
5 最后,关闭读管道句柄。

 

 

http://www.adintr.com/article/278

Windows管道(Pipe)重定向stdout,stderr,stdin

by other 
March 25, 2012, 1:50 p.m.

stdin是正规输入,stdout是正式输出,stderr是正式错误输出。
大多数底吩咐行程序从stdin输入,输出及stdout或stderr,有时我们需要重定向stdout,stderr,stdin。比如:将出口写副文件,又或我们若以命行程序输出结果显示到Windows对话框中。

在Windows编程中,重定向需要因此到管道(Pipe)的定义。管道是同栽用于在过程中共享数据的机制。一个管道类似于一个管子的双边,一端是形容副的,一端是读出的。由一个进程从写入端写入、另一个历程从读出端读来,从而实现通信,就朝着一个“管道”一样。

重定向的法则是:

率先声明两只概念:主程序(重定向的掌握者)、子进程(被另行定向的子进程)

倘要重复一贯stdout的讲话,先生化作一个管道,
管道的写照入端交给子进程去写,主程序从管道的诵读出端读数据,然后可以将多少形容成文件、显示等等。重定向stderr和stdout是一样的。

同理,要重定向stdin的口舌,生成一个管道,
管道的勾入端由主程序写,子进程从管道的朗读出端读数据。

其间要动用几单Windows API :  CreatePipe, DuplicateHandle,
CreateProcess, ReadFile, WriteFile 等,函数详解可参见MSDN.

一律、编程实现原理 ( C语言)

[html] view
plaincopy

 

  1. #include <windows.h>  
  2.   
  3.   //定义句子柄: 构成stdin管道的蝇头端句柄  
  4.   HANDLE  hStdInRead;         //子进程之所以的stdin的朗读入端  
  5.   HANDLE  hStdInWrite;        //主程序用底stdin的读入端  
  6.   
  7.   //定义词柄: 构成stdout管道的鲜端句柄  
  8.   HANDLE  hStdOutRead;     ///主程序用之stdout的诵读入端  
  9.   HANDLE  hStdOutWrite;    ///子进程之所以的stdout的抒写入端  
  10.   
  11.   //定义词柄: 构成stderr管道的句柄,由于stderr一般等stdout,我们无定义hStdErrRead,直接用hStdOutRead即可  
  12.   HANDLE  hStdErrWrite;       ///子进程之所以底stderr的形容入端  
  13.   
  14.   //定义一个用来产生子进程的STARTUPINFO结构体 (定义见CreateProcess,函数说明)  
  15.   STARTUPINFO siStartInfo;  
  16.   //定义一个用于产生子进程的PROCESS_INFORMATION结构体 (定义见CreateProcess,函数说明)  
  17.   PROCESS_INFORMATION piProcInfo;  
  18.   
  19.   
  20.   //产生一个用以stdin的管道,得到两只HANDLE:  hStdInRead用于子进程读来多少,hStdInWrite用于主程序写副数据  
  21.   //其中saAttr是一个STARTUPINFO结构体,定义见CreatePipe函数说明  
  22.   if   (!CreatePipe(&hStdInRead, &hStdInWrite,&saAttr, 0))  
  23.           return;  
  24.   
  25.   //产生一个用来stdout的管道,得到两只HANDLE:  hStdInRead用于主程序读来多少,hStdInWrite用于子程序写副数据  
  26.   if  (!CreatePipe(&hStdOutRead, &hStdOutWrite,&saAttr, 0))  
  27.         return;  
  28.   //由于stderr一般就是stdout, 直接复制句柄hStdOutWrite,得到 hStdErrWrite  
  29.    if (!DuplicateHandle(GetCurrentProcess(), hStdOutWrite, GetCurrentProcess(), &hStdErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))  
  30.       return;  
  31.   
  32.   //对STARTUPINFO结构体赋值,对stdin,stdout,stderr的Handle设置为刚刚获得的管道HANDLE  
  33.   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );  
  34.   siStartInfo.cb = sizeof(STARTUPINFO);  
  35.   siStartInfo.dwFlags  |= STARTF_USESHOWWINDOW;  
  36.   siStartInfo.dwFlags  |= STARTF_USESTDHANDLES;  
  37.   siStartInfo.hStdOutput = hStdOutWrite;     //意思是:子进程的stdout输出到hStdOutWrite  
  38.   siStartInfo.hStdError  =  hStdErrWrite;        //意思是:子进程的stderr输出及hStdErrWrite  
  39.   siStartInfo.hStdInput  = hStdInRead;  
  40.   
  41.   // 产生子进程,具体参数说明见CreateProcess函数  
  42.   bSuccess = CreateProcess(NULL,  
  43.       CommandLine,    // 子进程的授命行  
  44.       NULL,                   // process security attributes  
  45.       NULL,                   // primary thread security attributes  
  46.       TRUE,                   // handles are inherited  
  47.       0,                          // creation flags  
  48.       NULL,                  // use parent’s environment  
  49.       NULL,                  // use parent’s current directory  
  50.       &siStartInfo,      // STARTUPINFO pointer  
  51.       &piProcInfo);     // receives PROCESS_INFORMATION  
  52.   
  53.    //如果失败,退出  
  54.    if (!bSuccess ) return;  
  55.   
  56.     //然后,就可读写管道了  
  57.     //写副stdin,具体代码在一个WriteToPipe函数中  
  58.     WriteToPipe();  
  59.   
  60.    //不断子检测过程来吗结束  
  61.     while (GetExitCodeProcess(piProcInfo.hProcess,&process_exit_code))  
  62.         {  
  63.             //读stdout,stderr  
  64.            ReadFromPipe();  
  65.            //如果子进程结束,退出循环  
  66.            if (process_exit_code!=STILL_ACTIVE) break;  
  67.         }  

 
切实看一下WriteToPipe(), ReadFromPipe函数:

[html] view
plaincopy

 

  1. //写入stdin  
  2. BOOL WriteToPipe()  
  3. {  
  4.   DWORD dwWritten;  
  5.   BOOL bSuccess = FALSE;  
  6.     
  7.  //用WriteFile,从hStdInWrite写副数据,数据以in_buffer中,长度为dwSize  
  8.   bSuccess = WriteFile( hStdInWrite, in_buffer, dwSize, &dwWritten, NULL);  
  9.   return bSuccess;  
  10. }  

 

[html] view
plaincopy

 

  1. // 读出stdout  
  2. BOOL ReadFromPipe()  
  3. {  
  4.   char out_buffer[4096];  
  5.   DWORD dwRead;    
  6.   BOOL bSuccess = FALSE;  
  7.   
  8.    //用WriteFile,从hStdOutRead读出子进程stdout输出的多少,数据结果当out_buffer中,长度为dwRead  
  9.    bSuccess = ReadFile( hStdOutRead, out_buffer, BUFSIZE, &dwRead, NULL);  
  10.    if ((bSuccess) && (dwRead!=0))  //如果成功了,且长度>0  
  11.              {  
  12.                    // 此处加入你自己之代码  
  13.                    // 比如:将数据写入文件要出示到窗口被  
  14.              }  
  15.   return bSuccess;  
  16. }  

OK,到这个原理写了了。为简化说明原理,上述代码省略了错处理、结束处理(如:CloseHandle等),具体可参见我之源码。

第二、封装、实用的代码

上述过程有些累,实际采用受到,我封装成几单函数:

率先定义三独转调函数 (就是函数指针类型)

//当stdin输入时,调用此函数。将要写的数码勾勒副buf中,*p_size写吧数长度即可。
typedef void FuncIn(char *buf,int *p_size);

//当stdout,stderr输出时,调用此函数。可读取的数以buf中,数据长度也size。
typedef void FuncOut(char *buf,int size);

//当子进程不断过程被,周期性调用此函数,设置p_abort可被断子进程。
typedef void FuncProcessing(int *p_abort);

然后定义了季单函数

//执行一个发令执行,重定向stdin, stdout,stderr。
//OnStdOut是回调函数指针,当有出口时,OnStdOut于调用。
//OnStdIn是回调函数指针,当输入时,OnStdIn被调用。
int ExecCommandEx(char *szCommandLine,char *CurrentDirectory,char
*Environment,unsigned short ShowWindow,
              FuncOut *OnStdOut,FuncProcessing *OnProcessing,FuncIn
*OnStdIn);

//执行一个下令执行,重定向stdout,stderr。
//OnLineOut是回调函数指针,当起平等实行输出时,OnLineOut给调用。
int ExecCommandOutput(char *szCommandLine,char *Environment,unsigned
short ShowWindow,
                       FuncOut *OnLineOut,FuncProcessing
*OnProcessing);

//执行一个限令执行,等待子进程结束,返回子进程的次第退出代码。不处理又定向。
int ExecCommandWait(char *szCommandLine,unsigned short
ShowWindow,FuncProcessing *OnProcessing);

//执行一个发令执行,不等待子进程结束,即返。不处理还定向。功能相当给
Windows API  WinExec.
int ExecCommandNoWait(char *szCommandLine,unsigned short ShowWindow);

尚定义了一个仓储数据的EXEC_INFO结构体及操作它们的函数。

一体代码为C语言,在JExecution.c, JExecution.h两只文件中。只所以到了Windows
API,没有就此MFC及另外库。

老三、使用办法

有了JExecution.c,使用就坏方便了。比如:

[html] view
plaincopy

 

  1. #include <stdio.h>  
  2. #include <windows.h>  
  3. #include “JExecution.h”  
  4.   
  5. //定义一个处理输出的回调函数  
  6. void OnLineOut(char *buf,int size)  
  7. {  
  8.    printf(“%s\n”, buf);  
  9. }  
  10.   
  11.   
  12. int main(int argc, char* argv[])  
  13. {  
  14.   int n;  
  15.   char *command;  
  16.   
  17.   command = “cmd.exe  /r dir/w “;   //这个命令的意就是是调用DOS,执行dir命令,显示当前手上产卵之公文  
  18.   n=ExecCommandOutput(command,NULL,SW_HIDE,OnLineOut,NULL);  
  19.   printf(“<Return:>%d”,n);  
  20. }  

相关文章