This article is currently in the process of being translated into Chinese (~91% done).
Starting applications with the Process class
程序的众多功能中有一项非常有用的是运行其它程序。比如,可能会在程序中包含一个指向相应网站的链接,如果不想强迫用户自己把此链接URL敲入浏览器或拷贝/粘贴进去,就应该让该链接可点击,然后用用户的默认浏览器打开它。
本教程主要使用的是控制台程序,因为它们相对简单,能更好地展示语言的句法而不用涉及过多繁杂的内容。而上文提到的例子明显更倾向于图形用户界面(GUI),需要使用象WinForms或WPF这样的.NET GUI框架,不过从一个控制台程序中运行其它程序仍然很有用,即使现在没有用,学会了也没坏处,总有一天会用到。
使用Process类
要实例化其它程序 ,需要使用Process类。Process类在System.Diagnostics名空间中,因此需要先引用:
using System.Diagnostics;最基本的用法是简单地使用静态方法Start()来运行一个程序 :
Process.Start("https://www.google.com/");注意此处提供的是一个URL - 这里也可以是一个本地程序的路径,如,Notepad(如在Microsoft Windows操作系统上):
Process.Start(@"C:\Windows\notepad.exe");两种方法都可行的原因是因为Process类仅仅是把此指令传递给操作系统,基本上等于对操作系统说“运行这个东西”。操作系统会检查传入的信息,看是否有支持此类信息的操作。如果传入的是个可执行文件的路径,就运行该程序 - 如果传入的是其它东西,如,一个URL(网络链接)或指向某类本地文件的路径,操作系统会试图使用某个相关的程序去处理这个文件或URL。这也表示在程序中可以运行一个指向本地文件夹的路径 - 如果用的是Windows操作系统,这会启动Windows文件浏览器(Windows File Explorer),并打开传入的路径。
使用参数启动程序
启动程序时能够传入一个或多个参数非常有用。控制台程序通常能够接收广泛的各类参数,不过Windows程序也能接收通过控制台传入的参数。比如,启动写字板(Notepad)程序时,可以传入需要打开编辑的文件作为参数,象这样:
Process.Start(@"C:\Windows\notepad.exe", @"C:\Windows\win.ini");可以这样是因为Notepad程序本身就设计为要监视控制台命令行以接收参数 - 如果启动时有参数,它会使用第一个参数作为要打开的文件的路径。可以简单地把win.ini替换为任何想要打开的文件来测试看看!
使用ProcessStartInfo类
Process类是个很复杂的类,功能远非仅仅是启动程序。本文不会涉及其全部功能,而是主要关注其启动新进程的能力。启动进程时,有时需要对其行为多一点控制,这就需要用到ProcessStartInfo类了。这样可以传入的参数就不象使用Start()方法参数一样仅传入一个路径了,而是传入一个ProcessStartInfo类实例,象这样:
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = @"C:\Windows\notepad.exe";
processStartInfo.Arguments = @"C:\Windows\win.ini";
Process.Start(processStartInfo);此代码其实与前面的例子功能完全一样,因此这里先介绍几个ProcessStartInfo类有用的属性,除了前面例子用到的Filename和Arguments属性之外,还有些属性能提供更多的控制:
WindowStyle
使用WindowStyle属性控制程序启动时的显示方式。例如,可以以最大化或最小化的方式启动程序,象这样:
processStartInfo.WindowStyle = ProcessWindowStyle.Minimized;CreateNoWindow
CreateNoWindow属性的功能由其名称可看出:设置为true时,程序启动时不会生成窗口。这当然只与那些不需要用户交互界面就可以完成其功能的程序相关,比如,一个可以接收输入,然后用这些输入做某些操作,并把结果返回给调用程序的程序。
UseShellExecute
此属性允许控制是否使用系统shell来启动进程 - 否则进程会直接由可执行文件生成。在.NET Core之前的.NET framework中,此属性的默认值是true,但在.NET Core中默认值是false。后面会在扩展示例中用到此属性。
WorkingDirectory
使用此属性设置可执行内容的启动目录路径。不过,要注意其作用根据UseShellExecute属性设置为true还是false会有所不同。当UseShellExecute设置为true时,WorkingDirectory属性只简单指定可执行文件的位置,且调用方的工作目录也会成为被启动的进程的工作目录。当UseShellExecute设置为false时,WorkingDirectory属性不用于查找可执行文件 - 而是把其传递给启动的进程,有什么功能由新进程的上正文自行确定。
RedirectStandardInput,RedirectStandardOutput和RedirectStandardError
这几个属性设置为true时,可以把被启动的进程的输入/输出及错误信息重定向到调用进程。这些属性,比如,在与CreateNoWindow一起使用时就会很有用,因为可以启动进程,然后给其提供输入和/或从中接收错误信息和输出。在后面的最终示例中综合使用前面提到的属性时也会用到此属性。
综合示例
可以讨论的属性其实很多,但前面强调的这些属性无疑是最重要的。因此,这里会展示最终示例,把这几个刚才介绍过的技术用于实现一些很有意思的功能。首先,请先看一下完整示例代码,下面就会开始讲解此示例:
Console.WriteLine("Press any key to run CMD...");
Console.ReadKey();
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = @"C:\Windows\system32\cmd.exe";
processStartInfo.Arguments = "/c date /t";
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
Process process = new Process();
process.StartInfo = processStartInfo;
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Console.WriteLine("Current date (received from CMD):");
Console.Write(output);示例开始时生成了一个ProcessStartInfo对象。使用Filename和Arguments属性指定运行一个Windows命令行程序,具体为cmd.exe(默认的Windows命令行解释器)。需要运行的命令由Arguments指定 - 这里指定了带/t参数的date命令,只简单输出当前日期。
注意此处使用了三个前面讨论过的属性:CreateNoWindow = true(希望不用显示cmd窗口就能获取其输出),UseShellExecute = false(需要其直接执行指定的命令)和RedirectStandardOutput = false(同样,需要获取其输出)。
然后,程序创建Process实例,设置其StartInfo属性值,调用Start()方法启动进程。
接下来两行代码通过调用Process.StandardOutput.ReadToEnd()方法请求接收产生的输出。把此输出保存在一个字符串变量中,然后调用WaitForExit()方法,确保被启动的这个进程有机会完全运行结束。
最后两行代码简单地把收到的输入信息输出 - 应该就是当前日期。当然,对于输出当前日期这个功能来说这样做显然太过繁琐 - 本来一行代码就能搞定的事。但这个例子能展示出这种技术的强大功能,允许调用其它程序,并在调用方程序中使用其功能。
总结
Process类是个非常强大的工具,允许启动其它进程,甚至可以进行控制。本文只展示了其众多能力中的一部分,不过希望已经展示出了此工具的强大之处,有兴趣可以自行进一步去探索其它的功能。