环境:win10 64位,vs2015 mfc程序

目的:记录CreateProcess()接收启动程序的输出。注:cmd程序,而不是带ui的程序

起始原由:通过调用git工具来完成自己的一些需求

参考:

https://bbs.csdn.net/topics/190138594,会读取控制台输出,但是如何向控制台输入数据?交互?
https://blog.csdn.net/waitig1992/article/details/23766833,MFC执行CMD命令并获得其返回信息源代码

之前几乎没有用过CreateProcess()函数,偶尔需要启动其它程序,一般用system(),也没有啥特殊要求,本次有点不一样,需要接收git的输出,以便进行分析其结果。先上个代码,由于代码是多次调试的结果,所以看上去可能会有点乱,但不影响分析过程。

void LoadGitDirs()
{CString strText;PROCESS_INFORMATION pi;SECURITY_ATTRIBUTES sa;memset(&pi, 0, sizeof(pi));memset(&sa, 0, sizeof(sa));sa.nLength = sizeof(sa);sa.bInheritHandle = TRUE;HANDLE readPipe = NULL;HANDLE wrtePipe = NULL;if (!CreatePipe(&readPipe, &wrtePipe, &sa, 1024*20)){AfxMessageBox(TEXT("创建管道失败,无法继续!"), MB_ICONSTOP);return;}STARTUPINFO si;memset(&si, 0, sizeof(si));si.cb = sizeof(si);si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;si.wShowWindow = SW_HIDE;si.hStdOutput = wrtePipe;si.hStdError = wrtePipe;CString strCmdExe = TEXT("c:\\windows\\system32\\cmd.exe");DWORD dwErr = 0;CString strPars = m_strGitTool;//strPars = TEXT("cmd dir");strPars = TEXT(" /c ") + strPars;BOOL isOk = CreateProcess(strCmdExe, (LPTSTR)strPars.GetString(), &sa, &sa, TRUE, 0, NULL, NULL, &si, &pi);strPars.ReleaseBuffer();CloseHandle(wrtePipe);wrtePipe = NULL;       // 这里不使用输入if (!isOk){dwErr = GetLastError();CloseHandle(readPipe);readPipe = NULL;AfxMessageBox(TEXT("启动git命令失败,无法继续!"), MB_ICONSTOP);return;}// 开始等待结束DWORD dwWaitResult;DWORD dwReadLen = 0;CString strGitRes;char szBuf[1024 * 24] = { 0 };while (true){dwWaitResult = WaitForSingleObject(pi.hProcess, 200);if (dwWaitResult == WAIT_OBJECT_0 || dwWaitResult == WAIT_ABANDONED)break;if (ReadFile(readPipe, szBuf, 1024 * 20, &dwReadLen, NULL) && dwReadLen > 0){strGitRes += szBuf;}memset(szBuf, 0, sizeof(szBuf));dwReadLen = 0;}while(ReadFile(readPipe, szBuf, 1024 * 20, &dwReadLen, NULL) && dwReadLen > 0){strGitRes += szBuf;memset(szBuf, 0, sizeof(szBuf));dwReadLen = 0;}CloseHandle(pi.hProcess);CloseHandle(pi.hThread);CloseHandle(readPipe);readPipe = NULL;if (strGitRes != TEXT(""))AfxMessageBox(strGitRes, MB_ICONINFORMATION);
}

本段代码是调试通过的,m_strGitTool是git.exe的绝对路径,原工程用的是unicode编码,所以实际上在ReadFile()之后,szBuf是转换为unicode之后加到strGitRes上的,本文进行了简化,去掉了转换过程,所以如果试验此代码,需要在多字符编码下试验,如果是在unicode下,先对szBuf转换后再加到strGitRes上,否则最后的提示可能就是一堆乱码。

本代码只是调试通过,能正确接收git工具的输出,而在实际使用中是否还有其它问题,本文暂不考虑,因为我只是在调通之后来写这文章,还没有实际使用过,不知道中间是否会有其它一些问题。

以上程序的运行结果,就是弹出一个框,内容就是git的输出,如下所示:

参考的文章,以及搜索出来的其它文章,我觉得本身只是简单记录,并没有考虑的全面,但上面的代码,考虑了启动程序长时间运行时的情况(当然本文说的并不全面),每过200毫秒接收一次,直到程序退出后再继续接收直到接收不到内容为止。

本文主要记录调试过程中遇到的两个问题,另外再加上之前遇到的一些问题说明。

先说sa,即 SECURITY_ATTRIBUTES 结构。这个安全结构,早期的时候我都没用过,有它的地方,全都是设置成了NULL,当然那时候主要是线程上会用到。话说有一天,又在一个程序中创建了线程,最后发现有的电脑上正常运行,而有的电脑上运行失败,通过跟踪发现是创建线程失败,再反复查找问题,最终把安全参数传入的NULL换成了一个sa把解决问题了,当然也就是像上面代码中那样,定义变量-->清空所有成员-->设置长度成员,没有进行其它的设置,这是我记得很清楚的一件事,至于更深层的原因,当时就没有追究了,直到现在也没有再去追究过,只是知道通过这个方法解决了那个问题,所以后来,直到现在,只要是有这个参数的,我一律传入没有经过特殊设置的sa参数而不再传入NULL。

试验一:去掉 /c 参数

对strPars的修改如下:

 CString strPars = m_strGitTool;//strPars = TEXT(" /c");strPars = TEXT(" ") + strPars;

这个修改,是把上面的代码这一行分成了两行,同时注释掉参数/c。运行结果:

 while (true){dwWaitResult = WaitForSingleObject(pi.hProcess, 200);if (dwWaitResult == WAIT_OBJECT_0 || dwWaitResult == WAIT_ABANDONED)break;if (ReadFile(readPipe, szBuf, 1024 * 20, &dwReadLen, NULL) && dwReadLen > 0){gStr2U(szBuf, strText);strGitRes += strText;}memset(szBuf, 0, sizeof(szBuf));dwReadLen = 0;}

这个while循环运行了一次,而且ReadFile成功,strGitRes的内容为:“Microsoft Windows [版本 10.0.18363.1139]\r\n(c) 2019 Microsoft Corporation。保留所有权利。\r\n\r\nE:\\myproject\\projectname>”,当循环第二次时,程序卡在了 ReadFile() 这一行上,只好终止调试。

试验二:

把关闭写的管道放在第二个while之后,像下面这样子:

 CloseHandle(wrtePipe);wrtePipe = NULL;     // 这里不使用输入CloseHandle(pi.hProcess);CloseHandle(pi.hThread);CloseHandle(readPipe);readPipe = NULL;

注意成功的代码中,这个关闭是放在CreateProcess()后面的。现在这样修改之后,第一个while循环会直接break,第二个while的ReadFile()会卡住,程序无法继续往下走。

第三点,注意对CreateProcess()参数si的输出赋值,

 si.hStdOutput = wrtePipe;si.hStdError = wrtePipe;

输出应该是赋给写管道的句柄,刚开始我把它赋成了读管道的句柄,一直收不到任何信息。这个读写,是对启动程序来说的,对于宿主程序,则是相反的理解。

第四点,注意对CreateProcess()参数si的dwFlags赋值,这点非常重要,成功时的赋值如下(如第一段代码中):

 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;si.wShowWindow = SW_HIDE;

我没有仔细研究其它的项,仅说明我遇到的情况。
STARTF_USESTDHANDLES:比较好理解,就是要用本结构中的输入输出,当它们的值不为NULL时有效。
STARTF_USESHOWWINDOW:这个标志非常重要,如果不设置,则后面的ReadFile()全部失败,即接收不到任何的程序输出。如果设置了此标志,则其后的 wShowWindow就非常重要了,wShowWindow可取值同CreateWindow(),这里只讨论两个值:SW_HIDE和SW_SHOW,如果设置了SW_SHOW,则cmd的黑色窗口将会显示出来(当然对于命令立马就执行完的情况时,这个黑色窗口就是闪一下而已,也就是说,它是在启动程序运行的过程中出现,启动程序结束时窗口也自然结束),此时,宿主程序将接收不到任何启动程序的输出,所以此项必需设置为SW_HIDE。
对这两个成员的设置总结就是,dwFlags必需设置 STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW(至于是否可以加其它标志,并没有研究过),wShowWindow必需设置为SW_HIDE,否则宿主程序无法接收到启动程序的打印信息。

本程序的思路:在子程序启动后,在while循环中最长不超过200毫秒不断地检测子程序是否已经退出,如果已经退出,则结束此while循环,否则就读取一次打印,如果读取成功,就保存收到的打印信息。当第一个while循环退出后,就表示子程序已经结束了运行,此时再尝试继续接收剩余的打印,直到没有打印信息可接收时结束整个任务。

CreateProcess()接收程序的输出相关推荐

  1. Createprocess控制台程序输出重定向

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 在Win ...

  2. python执行结果在gui界面显示_Python PyQt5运行程序把输出信息展示到GUI图形界面上...

    概述:最近在赶毕业设计,遇到一个问题,爬虫模块我用PyQt5写了图形界面,为了将所有的输出信息都显示到图形界面上遇到了问题. 先演示一下效果最终效果吧,下面两张图用来镇楼.可以看到我们图形界面和程序运 ...

  3. 在JAVA中 以下程序_在Java中,以下程序的输出结果是()_学小易找答案

    [单选题]运行以下Java代码,说法正确的是( ). [单选题]在Java中,以下程序的输出结果是() [单选题]利用"出声"的方式向学生展示教师分析问题的过程与方法的教学方法是_ ...

  4. STM32-USART接收程序

    文章目录 对串口发送程序进行修改位接收 编译烧录后结果为 查询方式接收程序解读 中断方式接收程序解读 杨桃32学习笔记,本文图片文字皆为转述 对串口发送程序进行修改位接收 编译烧录后结果为 查询方式接 ...

  5. 在Linux程序中输出函数调用栈

    程序发生异常时,将函数的调用栈打印出来,可以大大提高定位效率. Linux中提供了三个函数用来获取调用栈: /* 获取函数调用栈 */ int backtrace(void **buffer, int ...

  6. Windows SDK程序的输出文字和格式控制(wsprintf、swprintf、Textout)

    程序的输出如上图: 输出相关的代码如下: TCHAR szBuffer [40] ; TCHAR szHello[] = TEXT("Hello, C-Free!"); int i ...

  7. 基础问题:在一个 Activity 中定义的串口接收程序,如果 Activity 切换到其它 Activity 后还能接收到串口数据吗?...

    ====================问题描述==================== RT:基础问题:在一个 Activity 中定义的串口接收程序,如果 Activity 切换到其它 Activ ...

  8. fork()请问下面的程序一共输出多少个“A”?多少个-?

    题目:请问下面的程序一共输出多少个"-"? #include #include #include int main(void) { int i; for(i=0; i<2; ...

  9. c语言if else语句_查找C程序的输出(如果为else语句)| 设置1

    c语言if else语句 Find the output of the following programs, 查找以下程序的输出, Program 1) 程序1) #include <stdi ...

最新文章

  1. 大数据运行环境的运行
  2. Boost:基于boost::asio单元测试的测试程序
  3. 用框架的你,可能早已忽略了这些事件API
  4. 70%以上程序员,不懂数据结构和算法!
  5. php声波模拟开门,关于 php使用扩展控制树莓派io 驱动超声波测距
  6. 图像标注,三倍加速:谷歌AI新方案,数据民工的福音 | Demo可玩耍
  7. [转载] Java异常:选择Checked Exception还是Unchecked Exception?
  8. 【数据库原理实验(openGauss)】数据库的备份与恢复
  9. 字节跳动单点恢复功能及 Regional CheckPoint 优化实践
  10. sqlyog 注册码
  11. 小白学电脑计算机的组成,新手学电脑步骤,从零开始学电脑
  12. STM32F103学习笔记(9)——NB-IoT模块BC26使用
  13. Windows 7 查看默认的本地 DNS 服务器地址
  14. 036--python--摇骰子游戏
  15. win10怎么更新显卡驱动_win10系统AMD显卡驱动安装失败的解决方法
  16. 【BZOJ1135】[POI2009]Lyz 线段树
  17. 2022年亚太杯数学建模竞赛ABC题
  18. Plague Inc
  19. 【open3d】安装open3d.whl之后,import报错ModuleNotFoundError: No module named ‘open3d.cpu‘
  20. [附源码]Java计算机毕业设计SSM动物园动物饲养管理

热门文章

  1. Java | 如何实现 Android 应用的图标徽章
  2. springboot 自动化装配机制(一)
  3. oppo android多大内存,友盟+发布移动应用性能体验报告:安卓阵营OPPO崩溃率最低...
  4. 踏遍青山情未老 —— 九山顶重游记(一)
  5. Linux(ubuntu) LNMP环境搭建
  6. 国密SSL加密证书获取流程
  7. 看完这篇学不会 Vim 定位操作我跟你姓!
  8. 第七十八章 SQL函数 $LENGTH
  9. 猴子爬山编程java_小猴子摘桃子問題 -- JAVA 算法學習
  10. Unity之图片轮播组件实现