CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
可用分押宝游戏火热进行中... 专题改版:Java Web 专题
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  VC/MFC >  进程/线程/DLL

怎么获得控制台程序输出

楼主gooyan(超级替补)2005-06-02 20:44:53 在 VC/MFC / 进程/线程/DLL 提问

假设我在GUI程序中执行ping   命令,怎么把ping命令产生的控制台输出   在GUI程序中获得.  
  我现在是用ShellExecute执行的ping命令  
  HINSTANCE   hInstance   =   ShellExecute(NULL,NULL,"ping.exe","10.57.18.166",NULL,SW_SHOW);  
  怎么获得输出?  
  如果这种方法不行,那用哪种方法可以获得输出?  
   
  我不想用>a.txt管道形式先输出到文件,然后再读取,我想直接获得,谢谢 问题点数:20、回复次数:6Top

1 楼krh2001(边城浪子)回复于 2005-06-02 20:54:18 得分 5

用管道重定义标准输出.   用   CreateProcess   可以指定控制台程序的输出流Top

2 楼oyljerry(【勇敢的心】→ ㊣提拉米苏√㊣)回复于 2005-06-02 21:57:06 得分 5

输出管道重定向Top

3 楼krh2001(边城浪子)回复于 2005-06-02 22:00:08 得分 10

在Windows环境下的所谓shell程序就是dos命令行程序,比如VC的CL.exe命令行编译器,JDK的javac编译器,启动java程序用的java.exe都是标准的shell程序。截获一个shell程序的输出是很有用的,比如说您可以自己编写一个IDE(集成开发环境),当用户发出编译指令时候,你可以在后台启动shell   调用编译器并截获它们的输出,对这些输出信息进行分析后在更为友好的用户界面上显示出来。为了方便起见,我们用VB作为本文的演示语言。    
     
  通常,系统启动Shell程序时缺省给定了3个I/O信道,标准输入(stdin),   标准输出stdout,   标准错误输出stderr。之所以这么区分是因为在早期的计算机系统如PDP-11的一些限制。那时没有GUI,   将输出分为stdout,stderr可以避免程序的调试信息和正常输出的信息混杂在一起。    
     
  通常,   shell程序把它们的输出写入标准输出管道(stdout)、把出错信息写入标准错误管道(stderr)。缺省情况下,系统将管道的输出直接送到屏幕,这样一来我们就能看到应用程序运行结果了。    
     
  为了捕获一个标准控制台应用程序的输出,我们必须把standOutput和standError管道输出重定向到我们自定义的管道。    
     
  下面的代码可以启动一个shell程序,并将其输出截获。    
   
   
   
   
  '执行并返回一个命令行程序(shell程序)的标准输出和标准错误输出  
  '通常命令行程序的所有输出都直接送到屏幕上  
  Private   Function   ExecuteApp(sCmdline   As   String)   As   String  
          Dim   proc   As   PROCESS_INFORMATION,   ret   As   Long  
          Dim   start   As   STARTUPINFO  
          Dim   sa   As   SECURITY_ATTRIBUTES  
          Dim   hReadPipe   As   Long   '负责读取的管道  
          Dim   hWritePipe   As   Long   '负责Shell程序的标准输出和标准错误输出的管道  
          Dim   sOutput   As   String   '放返回的数据  
          Dim   lngBytesRead   As   Long,   sBuffer   As   String   *   256  
           
          sa.nLength   =   Len(sa)  
          sa.bInheritHandle   =   True  
   
          ret   =   CreatePipe(hReadPipe,   hWritePipe,   sa,   0)  
           
          If   ret   =   0   Then  
                  MsgBox   "CreatePipe   failed.   Error:   "   &   Err.LastDllError  
                  Exit   Function  
          End   If  
           
          start.cb   =   Len(start)  
          start.dwFlags   =   STARTF_USESTDHANDLES   Or   STARTF_USESHOWWINDOW  
          '   把标准输出和标准错误输出重定向到同一个管道中去。  
          start.hStdOutput   =   hWritePipe  
          start.hStdError   =   hWritePipe  
          start.wShowWindow   =   SW_HIDE   '隐含shell程序窗口  
          '   启动shell程序,   sCmdLine指明执行的路径  
          ret   =   CreateProcessA(0&,   sCmdline,   sa,   sa,   True,   NORMAL_PRIORITY_CLASS,   _  
                          0&,   0&,   start,   proc)  
          If   ret   =   0   Then  
                  MsgBox   "无法建立新进程,错误码:"   &   Err.LastDllError  
                  Exit   Function  
          End   If  
          '   本例中不必向shell程序送信息,因此可以先关闭hWritePipe  
          CloseHandle   hWritePipe  
          '   循环读取shell程序的输出,每次读取256个字节。  
          Do  
                  ret   =   ReadFile(hReadPipe,   sBuffer,   256,   lngBytesRead,   0&)  
                  sOutput   =   sOutput   &   Left$(sBuffer,   lngBytesRead)  
          Loop   While   ret   <>   0   '   如果ret=0代表没有更多的信息需要读取了  
           
          '   释放相关资源  
          CloseHandle   proc.hProcess  
          CloseHandle   proc.hThread  
          CloseHandle   hReadPipe  
          ExecuteApp   =   sOutput   '   输出结果  
  End   Function  
   
   
   
  我对这个程序进行一些解释。    
     
  ret   =   CreatePipe(hReadPipe,   hWritePipe,   sa,   0)    
     
  大家可以看到,首先我们建立一个匿名管道。该匿名管道稍候将用来取得与被截获的应用程序的联系。其中hReadPipe用来获取shell程序的输出,而hWritePipe可以用来向应用程序发送信息。如同现实世界中的水管一样,水从管道的一端流进从另一端流出。您把水想象为信息,水管就是匿名管道,这样一来就很好理解这段程序了。    
  然后就是设置shell应用程序的初始属性。   Dwflags可以指示系统在创建新进程时新进程使用了自定义的wShowWindow,   hStdInput,hStdOutput和hStdError。(windows显示属性,标准输入,标准输出,标准错误输出。)    
  再把shell应用程序的标准输出和标准错误输出都定向到我们预先建好的管道中。    
  代码如下:    
     
  start.dwFlags   =   STARTF_USESTDHANDLES   Or   STARTF_USESHOWWINDOW    
  start.hStdOutput   =   hWritePipe    
  start.hStdError   =   hWritePipe    
  好,现在可以调用建立新进程的函数了:    
  ret   =   CreateProcessA(0&,   sCmdline,   sa,   sa,   True,   NORMAL_PRIORITY_CLASS,   0&,   0&,   start,   proc)    
  然后,循环读管道里的数据直到无数据可读为止。      
  Do    
  ret   =   ReadFile(hReadPipe,   sBuffer,   256,   lngBytesRead,   0&)   '每次读256字节    
  sOutput   =   sOutput   &   Left$(sBuffer,   lngBytesRead)   '送入一个字符串中    
  Loop   While   ret   <>   0   '若   ret   =   0   表明没有数据等待读取。    
  然后,释放不用的资源。    
     
  用法很简单:比如:    
  MsgBox   ExecuteApp("c:\windows\command\mem.exe)      
     
  是很方便吧?    
  不过,这些程序是在NT下的,如果要在95下实现还需要一点点改动。因为如果该函数调用一个纯win32的程序,没问题。可是95是16,win32混合的系统,当你试图调用一个16位的DOS应用程序那么,那么这个办法会导致相关进程挂起。因为这涉及到WindowsNT和Windows   95对shell的不同实现。    
  在win95中,16位shell程序关闭时并不保证重定向的管道也关闭,这样,当你的程序试图读取一个已经关闭的shell程序的重定向管道时,你的程序就挂了。    
  那么,有解决办法吗?回答是肯定的。    
  解决办法就是用一个win32的应用程序作为您的应用程序和shell程序的中间人。中间人程序继承并重定向了主程序的输入输出,然后中间人程序启动指定的shell程序。该shell程序也就继承并重定向了主程序的输入输出。中间人程序一直等到shell程序结束才结束。    
  当shell程序结束时,中间人程序也结束,同时因为中间人程序是一个win32程序,那么它就会关闭相应的重定向了管道。这样,你的程序可以发现管道已经关闭,便可以跳出循环。你的程序就不会挂起了。    
  下面是相关的中间人程序C代码的实现:    
     
  #include   <windows.h>  
  #include   <stdio.h>  
  void   main   (int   argc,   char   *argv[])  
  {  
    BOOL   bRet   =   FALSE;    
  STARTUPINFO   si   =   {0};    
  PROCESS_INFORMATION   pi   =   {0};    
  //   Make   child   process   use   this   app's   standard   files.    
  si.cb   =   sizeof(si);    
  si.dwFlags   =   STARTF_USESTDHANDLES;    
  si.hStdInput   =   GetStdHandle   (STD_INPUT_HANDLE);    
  si.hStdOutput   =   GetStdHandle   (STD_OUTPUT_HANDLE);    
  si.hStdError   =   GetStdHandle   (STD_ERROR_HANDLE);    
  bRet   =   CreateProcess   (NULL,   argv[1],   NULL,   NULL,   TRUE,   0,   NULL,   NULL,   &si,   &pi   );    
  if   (bRet)    
  {    
  WaitForSingleObject   (pi.hProcess,   INFINITE);  
  CloseHandle   (pi.hProcess);    
  CloseHandle   (pi.hThread);    
  }  
  }    
   
  把该程序编译为conspawn.exe并放在系统可以调用到的路径目录中。    
  然后把文章开头提到的代码中的CreateProcessA语句改为:    
   
  ret   =   CreateProcessA(0&,   "conspawn   """   &   sCmdline   &   """",   sa,   sa,   True,    
  NORMAL_PRIORITY_CLASS,   0&,   0&,   start,   proc)    
   
  好,这样一来,我们这个函数可以同时很好的支持WindowsNT和Windows95/98了。    
  Top

4 楼gooyan(超级替补)回复于 2005-06-02 22:52:19 得分 0

hehe,搞定,参考了   detrox   的   Blog    
  Top

5 楼fzd999(花差花差)回复于 2005-06-03 15:32:24 得分 0

这个问题应该放在Dll版~Top

6 楼MikeChen2003(asdghgdhf)回复于 2005-06-08 14:12:22 得分 0

mark  
  Top

相关问题

  • 如何在程序中获得控制台程序返回或输出的信息?
  • 怎样在自已的程序中运行一个控制台程序,并获得其输出,
  • 控制台程序能否输出win32exe程序的printf函数。
  • 重发监视控制台程序输出的问题
  • win32程序中如何在控制台输出
  • MFC程序如何在控制台输出?
  • 如何更改控制台程序输出字体的颜色?
  • 请教如何实现再BCB6下调用其他控制台程序并且截获控制台输出。
  • 请问如何在控制台程序中更规则的输出数据?
  • 一个多线程的入门问题,控制台程序不输出

关键词

  • win32
  • 应用程序
  • shell
  • 控制台
  • 命令行
  • sa
  • 编译器
  • 输出
  • 程序
  • 管道

得分解答快速导航

  • 帖主:gooyan
  • krh2001
  • oyljerry
  • krh2001

相关链接

  • Visual C++类图书
  • Visual C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
世纪乐知(北京)网络技术有限公司 版权所有, 京 ICP 证 020026 号
北京创新乐知广告有限公司 提供技术支持
Copyright © 2000-2007, CSDN.NET, All Rights Reserved
GongshangLogo