【.net 深呼吸】启动一个进程并实时获取状态信息
地球人和火星人都知道,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 深呼吸】启动一个进程并实时获取状态信息的更多相关文章
- C#只启动一个进程的代码
把写内容过程中经常用到的内容做个收藏,如下的内容是关于C#只启动一个进程的内容.public partial class App : Application { protected override ...
- C#/WPF 仅启动一个进程实例
如何实现仅启动一个 WPF 进程实例,并在打开第二个时,自动唤起之前打开的进程. 1 代码入口 在 App.xaml.cs 文件中,重写 OnStartup 方法,并添加 Mutex 进程锁. /// ...
- (转)linux用文件锁实现保证一个程序只能启动一个进程
#include <stdio.h> #include <unistd.h>#include <fcntl.h>#include <errno.h>in ...
- Ubuntu安装deb时错误:“dpkg:错误:另外一个进程已经为 dpkg 状态数据库 加锁”解决
以下方式任选一个即可: 1.重启系统 2.执行(这种方式不要尝试,系统很容易挂) sudo rm /var/lib/dpkg/lock 然后执行修复 sudo dpkg --configure -a
- [js高手之路] html5 canvas动画教程 - 实时获取鼠标的当前坐标
有了前面的canvas基础之后,现在开始就精彩了,后面写的canvas教程都是属于综合应用,前面已经写了常用的canvas基础知识,参考链接如下: [js高手之路] html5 canvas系列教程 ...
- Qt ------ QProcess,启动外部进程,进程间通信
简介: 可用于完成启动外部程序,并与之交互通信. 启动一个进程的名字叫“program”,如果某进程的路径没有设置成环境变量,“program”需要包含路径 如果进程可以接收参数,参数叫“argume ...
- erlang supervisor中启动普通的进程
文字部分转自: http://1234n.com/?post/qou3eb supervisor的子进程 一开始使用supervisor的时候,我用的是init/1返回子进程规格列表的方式,并且所有子 ...
- Qt 进程和线程之一:运行一个进程和进程间通信
Qt提供了对进程和线程的支持.本节讲述了怎样在Qt应用程序中启动一个进程,以及几种常用的进程间通信方法.如果对进程和线程的概念不是很了解,可以看我的另一篇博客:[多进程和多线程的概念. 设计应用程序时 ...
- erlang创建100万个进程,每一个进程花费多少时间呢?
最近工作需要,需要先测试一下erlang启动进程的时间开销: 看了一片博客,感觉挺好的,学习erlang推荐http://www.blogjava.net/yongboy/ 于是参照他的文章里面的一个 ...
随机推荐
- 菜鸟Python学习笔记第一天:关于一些函数库的使用
2017年1月3日 星期二 大一学习一门新的计算机语言真的很难,有时候连函数拼写出错查错都能查半天,没办法,谁让我英语太渣. 关于计算机语言的学习我想还是从C语言学习开始为好,Python有很多语言的 ...
- 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文
阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ...
- ExtJS 4.2 业务开发(三)数据添加和修改
接上面的船舶管理业务,这里介绍添加和修改操作. 目录 1. 添加操作 2. 修改操作 3. 在线演示 1. 添加操作 1.1 创建AddShipWindow.js 在业务中的view目录下创建一个Ad ...
- 从零开始编写自己的C#框架(26)——小结
一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...
- mybatis_映射查询
一.一对一映射查询: 第一种方式(手动映射):借助resultType属性,定义专门的pojo类作为输出类型,其中该po类中封装了查询结果集中所有的字段.此方法较为简单,企业中使用普遍. <!- ...
- 读过MBA的CEO更自私?《哈佛商业评论》2016年第12期。4星
老牌管理杂志.每期都值得精度.本期我还是给4星. 以下是本书中的一些内容的摘抄: 1:他们发现在Airbnb上,如果客人姓名听起来像黑人,那么比名字像白人的客人的接受率会低16%.#45 2:对立组织 ...
- Spark Streaming+Kafka
Spark Streaming+Kafka 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端, ...
- 程序猿是如何解决SQLServer占CPU100%的
文章目录 遇到的问题 使用SQLServer Profiler监控数据库 SQL1:查找最新的30条告警事件 SQL2:获取当前的总报警记录数 有哪些SQL语句会导致CPU过高? 查看SQL的查询计划 ...
- [异常解决] ubuntukylin16.04 LTS中关于flash安装和使用不了的问题解决
http://www.linuxdiyf.com/linux/25211.html 归纳解决flash插件大法: 启动器中找到 软件更新,启动,点击 其它软件,把Canonical合作伙伴前方框 选上 ...
- Android之SQLite数据库篇
一.SQLite简介 Google为Andriod的较大的数据处理提供了SQLite,他在数据存储.管理.维护等各方面都相当出色,功能也非常的强大. 二.SQLite的特点 1.轻量级使用 SQLit ...