【.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/ 于是参照他的文章里面的一个 ...
随机推荐
- 关于textview显示特殊符号居中的问题
话说这是2017年的第一篇博客,也是一篇技术博客.先从简单的一篇解决问题开始吧,千里之行,始于足下! ------------------------------------------------- ...
- 微软发布正式版SQL Server 2016
微软于今天在SQL 官方博客上宣布 SQL Server 数据库软件的正式发布版本(GA),历时一年多,微软为该软件发布了多个公共预览版和候选版本,而今天最终版本终于上线了.在博客中,微软数据集团的企 ...
- Enterprise Solution 3.1 企业应用开发框架 .NET ERP/CRM/MIS 开发框架,C/S架构,SQL Server + ORM(LLBL Gen Pro) + Infragistics WinForms
行业:基于数据库的制造行业管理软件,包含ERP.MRP.CRM.MIS.MES等企业管理软件 数据库平台:SQL Server 2005或以上 系统架构:C/S 开发技术 序号 领域 技术 1 数据库 ...
- 如何进行python性能分析?
在分析python代码性能瓶颈,但又不想修改源代码的时候,ipython shell以及第三方库提供了很多扩展工具,可以不用在代码里面加上统计性能的装饰器,也能很方便直观的分析代码性能.下面以我自己实 ...
- Linux常用指令指南,终端装逼利器
最近搞了台Macbook Pro,就学习了一下Linux命令,在网上查了些资料,看了本书叫<快乐的 Linux 命令行>,里面涉及到了各个方面的命令. 在此将常用的整理出来,以备将来使用. ...
- WPF 普通属性变化通知
问题描述:使用ObservableCollection<OrderItem> source 给Datagrid.ItemsSource赋值,在后台更新source集合后,前台Datagri ...
- iOS自定义model排序
在开发过程中,可能需要按照model的某种属性排序. 1.自定义model @interface Person : NSObject @property (nonatomic,copy) NSStri ...
- SharePonit 2010 更改另存为列表模板的语言类型
从朋友处得来一个列表模板:AccessApplicationSharePoint.stp 将其通过:网站操作----网站设置----列表模板,上传进去.然后去创建列表,发现找不到此模板. 根据多年老司 ...
- Linux基础介绍【第三篇】
更改SSH服务端远程登录的配置 windows服务端的默认远程管理端口是3389,管理员用户是administrator,普通用户是guest.Linux的管理用户是root,普通用户默认有很多个,远 ...
- 使用Jenkins配置Git+Maven的自动化构建
Jenkins是一个开源的持续集成工具,应用Jenkins搭建持续集成环境,可以进行自动构建.自动编译和部署,非常方便. 在服务器比较少的情况下,Jenkins的优势并不明显,但是随着项目发展,服务器 ...