用户操作
[即时聊天] [发私信] [加为好友]
邓捷ID:normandj
1321次访问,排名2万外,好友0人,关注者0人。
temp
normandj的文章
原创 12 篇
翻译 0 篇
转载 10 篇
评论 1 篇
normandj的公告
永远不要嫌弃你的父母行动迟缓,因为你永远想象不出你小的时候他们是如何耐心地教你走路; 永远不要嫌弃你的父母学不会电脑,因为你永远不会知道在你小的时候他们是如何不厌其烦地教你认字;
最近评论
normandj:Option Explicit

Private Const NORMAL_PRIORITY_CLASS = &H20&

Private Const STARTF_USESTDHANDLES = &H100&

Private Const STARTF_USESHOWWINDOW = &H1……
文章分类
收藏
    相册
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    转载 如何在VB中截获shell程序的输出收藏

    新一篇: VB6语言脚本解释器 | 旧一篇: 解决Gftp不能显示中文FTP目录的方法

    在Windows环境下的所谓shell程序就是dos命令行程序,比如VC的CL.exe命令行编译器,JDK的javac编译器,启动java程序用的java.exe都是标准的shell程序。截获一个shell程序的输出是很有用的,比如说您可以自己编写一个IDE(集成开发环境),当用户发出编译指令时候,你可以在后台启动shell 调用编译器并截获它们的输出,对这些输出信息进行分析后在更为友好的用户界面上显示出来。为了方便起见,我们用VB作为本文的演示语言。       在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 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和Windows98了。

    最终解决的代码:

    Option Explicit

    Private Const NORMAL_PRIORITY_CLASS = &H20&

    Private Const STARTF_USESTDHANDLES = &H100&

    Private Const STARTF_USESHOWWINDOW = &H1

    Private Type SECURITY_ATTRIBUTES

        nLength As Long     lpSecurityDescriptor As Long     bInheritHandle As Long

    End Type

    Private Type STARTUPINFO

        cb As Long     lpReserved As Long     lpDesktop As Long     lpTitle As Long     dwX As Long     dwY As Long     dwXSize As Long     dwYSize As Long     dwXCountChars As Long     dwYCountChars As Long     dwFillAttribute As Long     dwFlags As Long     wShowWindow As Integer     cbReserved2 As Integer     lpReserved2 As Long     hStdInput As Long     hStdOutput As Long     hStdError As Long

    End Type

    Private Type PROCESS_INFORMATION

        hProcess As Long     hThread As Long     dwProcessID As Long     dwThreadID As Long

    End Type

    Private Declare Function CreatePipe _                 Lib "kernel32" (phReadPipe As Long, _                                 phWritePipe As Long, _                                 lpPipeAttributes As SECURITY_ATTRIBUTES, _                                 ByVal nSize As Long) As Long

    Private Declare Function CreateProcess _                 Lib "kernel32" _                 Alias "CreateProcessA" (ByVal lpApplicationName As String, _                                         ByVal lpCommandLine As String, _                                         lpProcessAttributes As SECURITY_ATTRIBUTES, _                                         lpThreadAttributes As SECURITY_ATTRIBUTES, _                                         ByVal bInheritHandles As Long, _                                         ByVal dwCreationFlags As Long, _                                         lpEnvironment As Any, _                                         ByVal lpCurrentDriectory As String, _                                         lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long

    Private Declare Function ReadFile _                 Lib "kernel32" (ByVal hFile As Long, _                                 ByVal lpBuffer As String, _                                 ByVal nNumberOfBytesToRead As Long, _                                 lpNumberOfBytesRead As Long, _                                 ByVal lpOverlapped As Long) As Long

    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

    '************************************************************************* '**函 数 名:GetShellOutput '**输    入:ByVal Command(String) - Dos命令 '**输    出:(String) - 返回结果 '**功能描述:截取shell执行dos命令的返回结果 '**全局变量: '**调用模块: '**作    者: '**日    期:2008-06-27 09:32:06 '**修 改 人: '**日    期: '**版    本:V1.0.88 '************************************************************************* Public Function GetShellOutput(ByVal Command As String) As String

        Dim Proc         As PROCESS_INFORMATION '进程信息

        Dim Start        As STARTUPINFO '启动信息

        Dim SecAttr      As SECURITY_ATTRIBUTES '安全属性

        Dim hReadPipe    As Long '读取管道句柄

        Dim hWritePipe   As Long '写入管道句柄

        Dim lngBytesRead As Long '读出数据的字节数

        Dim strBuffer    As String * 256 '读取管道的字符串buffer

        Dim ret          As Long 'API函数返回值

        Dim lpOutputs    As String '读出的最终结果         On Error GoTo Errorhandler

        '设置安全属性     With SecAttr         .nLength = LenB(SecAttr)         .bInheritHandle = True         .lpSecurityDescriptor = 0     End With

        '创建管道     ret = CreatePipe(hReadPipe, hWritePipe, SecAttr, 0)

        If ret = 0 Then         MsgBox "无法创建管道", vbExclamation, "错误"

            Exit Function

        End If

        '设置进程启动前的信息     With Start         .cb = LenB(Start)         .dwFlags = STARTF_USESHOWWINDOW Or STARTF_USESTDHANDLES         .hStdOutput = hWritePipe '设置输出管道         .hStdError = hWritePipe '设置错误管道     End With

        '启动进程     ret = CreateProcess(vbNullString, Command, SecAttr, SecAttr, True, NORMAL_PRIORITY_CLASS, ByVal 0, vbNullString, Start, Proc)

        If ret = 0 Then         MsgBox "无法启动新进程", vbExclamation, "错误"         ret = CloseHandle(hWritePipe)         ret = CloseHandle(hReadPipe)

            Exit Function

        End If

        '因为无需写入数据,所以先关闭写入管道。而且这里必须关闭此管道,否则将无法读取数据     ret = CloseHandle(hWritePipe)

        '从输出管道读取数据,每次最多读取256字节     Do         ret = ReadFile(hReadPipe, strBuffer, 256, lngBytesRead, ByVal 0)         lpOutputs = lpOutputs & Left(strBuffer, lngBytesRead)

            DoEvents     Loop While (ret <> 0) '当ret=0时说明ReadFile执行失败,已经没有数据可读了

        '读取操作完成,关闭各句柄     ret = CloseHandle(Proc.hProcess)     ret = CloseHandle(Proc.hThread)     ret = CloseHandle(hReadPipe)         GetShellOutput = lpOutputs Exit Function Errorhandler:     MsgBox "错误信息:" & Err.Description, 16, "错误"     GetShellOutput = "" End Function

    发表于 @ 2008年06月26日 17:42:00|评论(loading...)|编辑|收藏

    新一篇: VB6语言脚本解释器 | 旧一篇: 解决Gftp不能显示中文FTP目录的方法

    评论

    #normandj 发表于2008-06-27 09:34:09  IP: 222.84.66.*
    Option Explicit

    Private Const NORMAL_PRIORITY_CLASS = &H20&

    Private Const STARTF_USESTDHANDLES = &H100&

    Private Const STARTF_USESHOWWINDOW = &H1

    Private Type SECURITY_ATTRIBUTES

    nLength As Long
    lpSecurityDescriptor As Long
    bInheritHandle As Long

    End Type

    Private Type STARTUPINFO

    cb As Long
    lpReserved As Long
    lpDesktop As Long
    lpTitle As Long
    dwX As Long
    dwY As Long
    dwXSize As Long
    dwYSize As Long
    dwXCountChars As Long
    dwYCountChars As Long
    dwFillAttribute As Long
    dwFlags As Long
    wShowWindow As Integer
    cbReserved2 As Integer
    lpReserved2 As Long
    hStdInput As Long
    hStdOutput As Long
    hStdError As Long

    End Type

    Private Type PROCESS_INFORMATION

    hProcess As Long
    hThread As Long
    dwProcessID As Long
    dwThreadID As Long

    End Type

    Private Declare Function CreatePipe _
    Lib "kernel32" (phReadPipe As Long, _
    phWritePipe As Long, _
    lpPipeAttributes As SECURITY_ATTRIBUTES, _
    ByVal nSize As Long) As Long

    Private Declare Function CreateProcess _
    Lib "kernel32" _
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © normandj