调用第三方应用程序
执行一个外部的第三方应用的原理,和调用cmd执行命令是一样的。只不过可以添加程序的启动参数(StartInfo.Arguments
)等。
调用其他应用程序,和调用cmd执行命令行也有所区别。通常调用cmd是为了执行完某条或某几条命令,执行完就结束了。而调用第三方应用,在其启动后,关于程序的操作、处理、是否有输入和输出,不同的程序有着不同的情况,不同需求和不同人的操作也会不同。可以尝试使用之前cmd的调用代码执行第三方程序,就能感受到很大不同,尤其是StandardOutput
读取输出,会一直等待。
因此,调用第三方程序,更多的是启动第三方程序,然后让用户去操作;或者针对某特定的程序,启动后通过代码准确执行特定的步骤,即自动化操作,然后关闭应用(这时涉及到自动化的部分[ui自动化、输入输出自动化]),甚至需要多线程,当前线程使用Process
打开应用,另一个线程操作应用等。
所以,目前难以有比较通用的方式,但是可以写一个启动方法,启动第三方软件,设置等不等待软件运行结束、获不获取输出(如果有的话)。后面更多的应该了解Process
的使用。
如下,采用下面的方式启动一个可执行程序。根据需要是否有启动参数、是否等待结束、是否获取输出等:
/// <summary>
/// 执行外部exe程序
/// </summary>
public static class ExecApp
{
/// <summary>
/// 启动一个可执行程序,执行参数为arguments。启动一个关联的子进程,并且等待子进程的处理返回【一种有限的方式运行其他程序】
/// 注意,如果是等待结束的情况下,必须保证会自动结束,或者最后会关闭/退出程序
/// </summary>
/// <param name="appPath"></param>
/// <param name="arguments">启动参数</param>
/// <param name="waitComplete">是否等待结束,默认false,启动程序后就结束不再管理;若为true,则必须保证会执行结束,或者手动关闭/退出程序,否则一直等待</param>
/// <param name="getOuput">是否获取输出结果,waitComplete为true时,true获取输出结果才有效,并且要确保程序有输出,否则会一直等待</param>
/// <returns></returns>
public static async Task<ExecResult> StartExecutAsync(string appPath,string arguments=null, bool waitComplete= false, bool getOuput=false)
{
using (Process p = new Process())
{
var result = new ExecResult();
try
{
p.StartInfo.FileName = appPath;
p.StartInfo.Arguments = arguments;
p.StartInfo.UseShellExecute = false; //设置false只能启动一个程序
p.StartInfo.RedirectStandardInput = false;
p.StartInfo.RedirectStandardOutput = getOuput; //由调用程序获取输出信息
p.StartInfo.RedirectStandardError = getOuput; //重定向标准错误输出
p.StartInfo.CreateNoWindow = false; //显示程序窗口
//p.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8;
p.Start();//启动程序执行
if (waitComplete)
{
if (getOuput)
{
// 获取输出【确保程序有输出,否则会一直等待】
result.Output = await p.StandardOutput.ReadToEndAsync();
result.Error = await p.StandardError.ReadToEndAsync();
}
p.WaitForExit();//等待程序执行完退出进程。应在最后调用
}
p.Close();
}
catch (Exception ex)
{
result.ExceptError = ex;
}
return result;
}
}
}
新建项目ExecAppTest
,用于测试执行外部程序。
如下测试执行。
//var result= await ExecApp.StartExecutAsync(@"C:\WINDOWS\system32\cmd.exe");
var result= await ExecApp.StartExecutAsync(@"C:\WINDOWS\system32\cmd.exe",waitComplete:true,getOuput:true);
MessageBox.Show($"{result.Output}");
Process进程类介绍和使用
Process打开一个文档或文件、启动一个程序
Process可以使用默认应用打开一个与其文件类型(后缀)关联的任何文档。比如打开以为文本文档、打开一个图片文件、打开一个url地址等。
Process.Start("www.bing.com");
Process.Start("my.png");
Process.Start(@"C:\WINDOWS\system32\cmd.exe");
Process.Start("explorer.exe", @"c:/"); //用资源管理器打开C:/
获取进程中的多个模块:
var mList = Process.GetCurrentProcess().Modules;
foreach (ProcessModule m in mList)
{
Console.WriteLine(string.Format("ModuleName is {0} \t ModuleURL is {1} \t ModuleVersion is {2}", m.ModuleName, m.FileName, m.FileVersionInfo.FileVersion));
}
ProcessStartInfo进程启动信息
ProcessStartInfo
用于设置进程其实的一些初始信息,比如启动参数(Arguments
)、工作目录(WorkingDirectory
)、是否使用系统Shell(UseShellExecute
)、是否显示窗口(CreateNoWindow
)\窗口的样式状态(WindowStyle
)、重定向输入输出、输出的编码(StandardOutputEncoding
)等
ProcessStartInfo Class可以查看更多的启动信息设置。
Process.StartInfo
属性就是ProcessStartInfo
,可以直接创建实例使用。
也可以创建ProcessStartInfo
对象,所谓进程Process.Start()
启动时的参数。
// 启动一个隐藏窗口的程序
ProcessStartInfo psi = new ProcessStartInfo()
{
FileName= fileName,
Arguments = arguments,
WindowStyle = ProcessWindowStyle.Hidden
};
Process.Start(psi);
ProcessStartInfo.UseShellExecute
、ProcessStartInfo.WorkingDirectory
和ProcessStartInfo.ErrorDialog
ProcessStartInfo.UseShellExecute=false
允许重定向输入、输出和错误流。但是,此时只能使用Process对象打开可执行程序。
UseShellExecute
的关键字shell表示图形shell(壳),而不是命令shell(如bash或sh),使用它可以让用户启动图形应用或打开文档。
UserName
非空时,UseShellExecute
必须为false,否则报错InvalidOperationException
。
- 当使用操作系统shell启动进程时(
UseShellExecute=true
),可以启动任何文档(包括可执行程序。只要注册关联了文件类型就行),并在文件上执行操作(比如使用Process对象打印)。
如果ErrorDialog=true
,则UseShellExecute必须为true;如果WindowStyle=ProcessWindowStyle.Hidden
,UseShellExecute也必须为true。
UseShellExecute 在.NET Framework中的默认值为true;.NET Core中为false。
ProcessStartInfo.ErrorDialog
表示进程无法启动时是否显示对话框提醒。
UseShellExecute
的取值不同,WorkingDirectory
也会有所不同。
如果UseShellExecute
为true, WorkingDirectory表示可执行程序的位置;
当UseShellExecute
为false
时,WorkingDirectory由启动的process对象使用。当UseShellExecute为false时,FileName
可以是可执行文件的绝对路径,也可以是系统PATH环境变量指定的文件夹中找到的简单的可执行程序名称。
也就是对于cmd的调用,useshellexecute=false
时,FileName
可以直接指定为cmd.exe
或cmd
即可。
WorkingDirectory
工作目录是一个非常拥有的属性,UseShellExecute为false时一定要指定正确(通常为可执行程序的所在路径,即要启动的进程的工作目录)。
进程退出、标准输出和错误输出事件
Process.OutputDataReceived
事件用于接受标准输出。上面的多个命令行执行的代码已有使用。Process.ErrorDataReceived
事件用于接受错误输出。上面的多个命令行执行的代码已有使用。Process.Exited
事件用于监听外部进程是否退出,可以避免WaitForExit()
的线程阻塞。在结束事件中处理结束后的操作。Exited
事件的启用,需要设置EnableRaisingEvents=true
。
比如下面,打开记事本程序,然后在Exited事件方法中处理其关闭后的操作,不阻塞当前线程的其他操作、
Process process = new Process();
process.StartInfo.FileName = @"C:\Windows\Notepad.exe";// process.StartInfo.FileName = "Notepad";
process.EnableRaisingEvents = true;
process.Exited += Process_Exited;
process.Start();
//...............
private void Process_Exited(object sender, EventArgs e)
{
//Exited事件处理代码,外部程序退出后激活,可以执行相关操作
MessageBox.Show("Notepad.exe运行完毕");
}