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

重定向所做的工作都在父进程,但需要子进程遵守下面的规则:

子进程程序在输出代码后,等待输入之前需要调用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 最后,关闭读管道句柄。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值