一片云雾

写博客挺浪费时间的……
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

调用命令行程序并获取返回信息

Posted on 2012-08-30 11:43  一片云雾  阅读(8032)  评论(5编辑  收藏  举报

调用命令行并获取返回信息是一件很有意思的事情,很多程序都会这么干,比如Visual Studio进行编译时,也是调用了一些命令行程序进行编译和链接,然后将其反馈信息输出到编译日志窗口。不过,调用一个命令行程序很简单,但获取其反馈信息并不是那么容易。当然如果知道怎么做,也不会很复杂。

思路如下:

  1. 创建一个匿名管道;
  2. 调用CreateProcess执行命令行程序并将其输出定位到匿名管道的写入端;
  3. 从匿名管道的读取端读取数据,那么读取到的数据就是命令行程序的输出信息;

这儿需要解释一下何谓管道,我的理解是:管道会提供一对端口,当向写入端口写入内容时,那么就可以在读取端口读取到相同的内容。匿名管道是管道中最简单的一种,通过调用CreatePipe函数可以创建一组匿名管道。

下面是一个调用命令行程序并获得返回值的C++函数源码:

 

std::string ExeCmd(const char * pszCmd)
{
    //创建匿名管道
    SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
    HANDLE hRead, hWrite;
    if (!CreatePipe(&hRead, &hWrite, &sa, 0))
    {
        return "";
    }

    //设置命令行进程启动信息(以隐藏方式启动命令并定位其输出到hWrite)
    STARTUPINFO si = {sizeof(STARTUPINFO)};
    GetStartupInfo(&si);
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.wShowWindow = SW_HIDE;
    si.hStdError = hWrite;
    si.hStdOutput = hWrite;

    //启动命令行
    PROCESS_INFORMATION pi;
    if (!CreateProcess(NULL, (char *)pszCmd, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
    {
        return "";
    }

    //立即关闭hWrite
    CloseHandle(hWrite);

    //读取命令行返回值
    std::string strRet;
    char buff[1024] = {0};
    DWORD dwRead = 0;
    while (ReadFile(hRead, buff, 1024, &dwRead, NULL))
    {
        strRet.append(buff, dwRead);
    }
    CloseHandle(hRead);

    return strRet;
}

 

这儿需要说明的是:为什么需要在启动命令行之后,会需要立即关闭hWrite?

原因是这样的:

首先,hWrite是Windows内核对象,内核对象有个特点,只有所有使用者都关闭该内核对象后,该内核对象才会真正关闭。这儿hWrite已经被上面的进程所使用了,所以此处关闭hWrite并不会导致该句柄失效。

其次,这样做有个好处在于,但命令行程序结束后,hWrite就会随之真正关闭。这样的话,才会让后续的ReadFile函数能够结束——否则它会一直等待直到hWrite被关闭(由于ReadFile是个同步函数,如果上面不关闭,以后永远没有关闭机会了)。

 

我在Visual Studio中,用一个对话框程序,简单测试一下这个函数:

 

void CTestPipeDlg::OnBnClickedOk()
{
    AfxMessageBox(ExeCmd("ping baidu.com").c_str());
}

 

结果如下: