地球人和火星人都知道,Process类既可以获取正在运行的进程,也可以启动一个新的进程。在79.77%应用场合,我们只需要让目标进程顺利启动就完事了,至于它执行了啥,有没有出错,啥时候退出就不管了。

但是,在某些情况下,启动新进程后,还希望能向目标进程传送数据,或者实时读取来自新进程的信息。比如,启动一个安装程序,安装程序会向标准流写入安装进度,然后调用方可以从标准流中读取进度,以达到实时监控安装进度的目的。

Process类公开三个标准流属性:

StandardInput——输入流。类型是Writer,为啥是writer呢,因为这个标准流是相对于被启动的进程而言的,流动方向是从调用方流向目标进程,所以是写入数据,即将内容发送到目标进程。

StandardOutput——输出流。即目标进程对外输出的内容,流动方向是从目标进程流向调用方,因此,对调用者来说,是读取,故其类型为Reader。

StandardError——和输入流差不多,只是它专用于输出错误。错误信息是目标进程输出的,所以,对调用者来说还是读取者。

综上所述,只要启动新进程后,从StandardOutput属性在得到一个StreamReader对象,然后建立一个循环,不断地从流中读取内容,就能够实时获得最新状态了。

其实,还有更好办的方法,Process类有个BeginOutputReadLine方法,调用后,会自动异步读取数据,一旦收到目标进程传回的数据,就会引发OutputDataReceived事。所以,我们在代码中只要处理这个事件就可以接收实时信息了。

咱们来做个例子吧。假设我弄一个程序,只负责在后台安装,每处理完一个进度,就会向标准流写入进度信息,这样调用者就能实时监控安装进度了。

首先完成被调用的项目,项目类型为Windows应用程序项目。

不管它,反正就是一个标准的.exe文件,这个项目我是先建个空白项目,然后手动设置的。

每个可执行程序都必须至少有一个Main方法。主要的处理代码都在这里完成,只要Main方法执行完,进程就会退出了。

        static void Main()
{
StreamWriter writer = null;
Stream outStream = Console.OpenStandardOutput();
writer = new StreamWriter(outStream); int n = ;
while (n <= )
{
Thread.Sleep();
writer.WriteLine(n);
writer.Flush();
n++;
}
writer.Close();
Environment.ExitCode = ;
}

这里用到了Console类,别以为它只能耍控制台应用程序,其实Console类还包括标准输入输出的操作。要调用OpenStandardOutput方法获取标准输出流,然后就可以写入内容了。

由于Process类的StandarOutput属性或OutputDataReceived事件,都是用字符串来传递的,所以上面代码中,咱们也用StreamWriter来写数据。

不过要注意一定,每写一回都要记得Flush一次,这样写入的内容才会让调用方及时收到。如果不Flush的话,写入的内容会放在缓冲区中,直接流关闭或执行Flush时才会真正发送到标准流上,所以,每写完一次都调用一下Flush方法,确保调用方能够实时收到信息。

最后那一行Environment.ExitCode = 0 表示进程退出时返回退出码0,即正常退出。因为我这个Main是返回void的,所以要用Enviroment类的ExitCode来设置。当然了,你还可以把Main方法改为返回int类型的值,然后直接 return 0 就行了。

好,被调用进程项目完成,现在做调用者项目,它是一个WPF项目。在这个时代,写Windows桌面应用都应优先用WPF,因为WPF是牛逼层面的东东。

XAML代码就不贴了,直接讲核心代码。

我用了个进度条来实时显示进度,而Process类的OutputDataReceived事件是异步引发的,要在事件处理中更新进度条,需要借助Dispatcher来代理调用。

不过,在这个例子中并不需要,因为有一个很NX的类,专门用来处理进度的,就是Progress<T>,这个类可以绑定一个回调的委托,用它来更新UI是不需要Dispatcher来调度的,只要Progress<T>实例是在UI线程上创建的即可(忘了说明这句,多谢网友在评论中补充)。

            IProgress<int> progress;

            progress = new Progress<int>(p =>
{
pb.Value = p;
});

在声明变量的时候,应用 IProcess<T> 接口来声明,T是表示进度的类型,Progress类是显示实现了IProgress接口的,为了能够直接调用Report方法报告进度,应当用IProgress接口来声明变量。

下面代码启动刚刚写的那个进程,并监视状态信息。

            Process p = new Process();
p.StartInfo.FileName = "..\\..\\..\\Demo\\bin\\Debug\\Demo.exe";
p.StartInfo.UseShellExecute = false; //必须为false
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.CreateNoWindow = true;
p.EnableRaisingEvents = true; //必须为true p.OutputDataReceived += OnDataReceived;
p.Exited += OnExited;
p.Start();
p.BeginOutputReadLine();

实例化Process后,要设置StartInfo相关参数,FileName是要执行进程的exe文件。

注意:

UseShellExecute必须为false,不然无法在代码中读标准。

RedirectStandardOutput必须为true,这样我们才能在代码中访问标准流。

EnableRaisingEvents必须为true,这样才会引发OutputDataReceived和Exited

事件,否则事件不会发生。

CreateNoWindow表示不显示目标程序的窗口,这个你自己看着办,这里我不让它显示窗口,因为这个程序本来就没有窗口。

一定要在Start方法之后调用BeginOutputReadLine方法,一定要在 Start 和 BeginOutputReadLine方法调用前处理OutputDataReceived事件。为啥,自己想吧,这太简单了,不脱裤子怎么拉屎,除非你不穿裤子。

在OnDataReceived方法中读出数据,并转化为int类型,因为刚才上面的那个项目中,是把一个int值写入流的,所以这里读出来的值是可以转换为int类型的。

        private void OnDataReceived(object sender, DataReceivedEventArgs e)
{
int p = Convert.ToInt32(e.Data);
progress.Report(p);
}

直接调用IProgress<T>的Report方法就能报告进度了。

本来,是可以调用 System.Diagnostics.Process.WaitForExit()方法来等待进程执行完的,但是,由于这个方法是同步调用的,它会让UI线程塞车,导致UI无法即时响应,体验不好。所以改为处理Exited事件,这个事件会在进程退出后异步调用,不会让UI线程塞车,所以处理它较好。

现在,运行例子,会看以下效果。

示例代码下载地址

【.net 深呼吸】启动一个进程并实时获取状态信息的更多相关文章

  1. C#只启动一个进程的代码

    把写内容过程中经常用到的内容做个收藏,如下的内容是关于C#只启动一个进程的内容.public partial class App : Application { protected override ...

  2. C#/WPF 仅启动一个进程实例

    如何实现仅启动一个 WPF 进程实例,并在打开第二个时,自动唤起之前打开的进程. 1 代码入口 在 App.xaml.cs 文件中,重写 OnStartup 方法,并添加 Mutex 进程锁. /// ...

  3. (转)linux用文件锁实现保证一个程序只能启动一个进程

    #include <stdio.h> #include <unistd.h>#include <fcntl.h>#include <errno.h>in ...

  4. Ubuntu安装deb时错误:“dpkg:错误:另外一个进程已经为 dpkg 状态数据库 加锁”解决

    以下方式任选一个即可: 1.重启系统 2.执行(这种方式不要尝试,系统很容易挂) sudo rm /var/lib/dpkg/lock 然后执行修复 sudo dpkg --configure -a

  5. [js高手之路] html5 canvas动画教程 - 实时获取鼠标的当前坐标

    有了前面的canvas基础之后,现在开始就精彩了,后面写的canvas教程都是属于综合应用,前面已经写了常用的canvas基础知识,参考链接如下: [js高手之路] html5 canvas系列教程 ...

  6. Qt ------ QProcess,启动外部进程,进程间通信

    简介: 可用于完成启动外部程序,并与之交互通信. 启动一个进程的名字叫“program”,如果某进程的路径没有设置成环境变量,“program”需要包含路径 如果进程可以接收参数,参数叫“argume ...

  7. erlang supervisor中启动普通的进程

    文字部分转自: http://1234n.com/?post/qou3eb supervisor的子进程 一开始使用supervisor的时候,我用的是init/1返回子进程规格列表的方式,并且所有子 ...

  8. Qt 进程和线程之一:运行一个进程和进程间通信

    Qt提供了对进程和线程的支持.本节讲述了怎样在Qt应用程序中启动一个进程,以及几种常用的进程间通信方法.如果对进程和线程的概念不是很了解,可以看我的另一篇博客:[多进程和多线程的概念. 设计应用程序时 ...

  9. erlang创建100万个进程,每一个进程花费多少时间呢?

    最近工作需要,需要先测试一下erlang启动进程的时间开销: 看了一片博客,感觉挺好的,学习erlang推荐http://www.blogjava.net/yongboy/ 于是参照他的文章里面的一个 ...

随机推荐

  1. 菜鸟Python学习笔记第一天:关于一些函数库的使用

    2017年1月3日 星期二 大一学习一门新的计算机语言真的很难,有时候连函数拼写出错查错都能查半天,没办法,谁让我英语太渣. 关于计算机语言的学习我想还是从C语言学习开始为好,Python有很多语言的 ...

  2. 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文

    阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ...

  3. ExtJS 4.2 业务开发(三)数据添加和修改

    接上面的船舶管理业务,这里介绍添加和修改操作. 目录 1. 添加操作 2. 修改操作 3. 在线演示 1. 添加操作 1.1 创建AddShipWindow.js 在业务中的view目录下创建一个Ad ...

  4. 从零开始编写自己的C#框架(26)——小结

    一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...

  5. mybatis_映射查询

    一.一对一映射查询: 第一种方式(手动映射):借助resultType属性,定义专门的pojo类作为输出类型,其中该po类中封装了查询结果集中所有的字段.此方法较为简单,企业中使用普遍. <!- ...

  6. 读过MBA的CEO更自私?《哈佛商业评论》2016年第12期。4星

    老牌管理杂志.每期都值得精度.本期我还是给4星. 以下是本书中的一些内容的摘抄: 1:他们发现在Airbnb上,如果客人姓名听起来像黑人,那么比名字像白人的客人的接受率会低16%.#45 2:对立组织 ...

  7. Spark Streaming+Kafka

    Spark Streaming+Kafka 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端, ...

  8. 程序猿是如何解决SQLServer占CPU100%的

    文章目录 遇到的问题 使用SQLServer Profiler监控数据库 SQL1:查找最新的30条告警事件 SQL2:获取当前的总报警记录数 有哪些SQL语句会导致CPU过高? 查看SQL的查询计划 ...

  9. [异常解决] ubuntukylin16.04 LTS中关于flash安装和使用不了的问题解决

    http://www.linuxdiyf.com/linux/25211.html 归纳解决flash插件大法: 启动器中找到 软件更新,启动,点击 其它软件,把Canonical合作伙伴前方框 选上 ...

  10. Android之SQLite数据库篇

    一.SQLite简介 Google为Andriod的较大的数据处理提供了SQLite,他在数据存储.管理.维护等各方面都相当出色,功能也非常的强大. 二.SQLite的特点 1.轻量级使用 SQLit ...