前言 Process类在System.Diagnostics命名空间中,用于启动和管理系统进程。
主要方法和属性如下:
Start() - 启动进程
Kill() - 终止进程
Close() - 仅仅是强制关闭了进程的标准流,但并没有停止进程的运行。
ExitCode - 获取进程退出代码
StartInfo - 设置启动进程的相关属性,如文件名、参数、工作目录等
StandardInput - 进程的标准输入流
StandardOutput - 进程的标准输出流
StandardError - 进程的标准错误流
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 Process process = new Process(); process.StartInfo.FileName = "cmd.exe" ; process.StartInfo.Arguments = "/c dir" ; process.StartInfo.CreateNoWindow = true ; process.StartInfo.UseShellExecute = false ; process.StartInfo.RedirectStandardOutput = true ; process.Start(); string output = p.StandardOutput.ReadToEnd(); Console.WriteLine(output); process.WaitForExit(); process.Dispose();
此代码启动cmd.exe,执行dir命令,并获取输出,输出目录列表信息。
StartInfo的主要属性:
FileName - 可执行文件的文件名
Arguments - 传递给可执行文件的命令行参数
WorkingDirectory - 进程工作目录
CreateNoWindow - 无窗口启动进程等等。使用Process类可以很方便的启动和管理系统进程。
UseShellExecute - 是否使用操作系统外壳启动
RedirectStandardInput / RedirectStandardOutput / RedirectStandardError - 重定向标准输入/输出/错误流
打开文件 使用系统的文件关联方式打开文件,如果没有关联的打开方式会抛出异常,要进行异常捕获。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private void OpenFile (string filepath ) { try { ProcessStartInfo psi = new ProcessStartInfo(filepath); Process pro = new Process { StartInfo = psi }; pro.Start(); } catch (Exception ex) { ZLogHelper.Logerror.Error("文件打开" , ex); MessageWindow.Show("文件打开失败,请设置文件的打开方式!" ); } }
打开文件夹 选中文件 1 Process.Start("explorer.exe" , $"/select,\"{mp4Path} \"" );
启动文件并等待安装 1 Process.Start(microsoftEdgeWebview2Setup, " /install" )?.WaitForExit();
打开本程序 1 Process.Start(Application.ResourceAssembly.Location);
应用更新 下面两种方式生效的前提是:
程序必须以管理员身份运行。
可参考 https://www.psvmc.cn/article/2020-07-31-wpf-run-admin.html 配置。
新进程中打开 注意:
程序安装升级包建议用这种方式
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 try { using (ManagementClass managementClass = new ManagementClass("Win32_Process" )) { ManagementClass processInfo = new ManagementClass("Win32_ProcessStartup" ); processInfo.Properties["CreateFlags" ].Value = 0x00000008 ; ManagementBaseObject inParameters = managementClass.GetMethodParameters("Create" ); inParameters["CommandLine" ] = filepath; inParameters["ProcessStartupInformation" ] = processInfo; ManagementBaseObject result = managementClass.InvokeMethod( "Create" , inParameters, null ); if ((result != null ) && ((uint )result.Properties["ReturnValue" ].Value != 0 )) { Console.WriteLine(@"Process ID: {0}" , result.Properties["ProcessId" ].Value); } } } catch (Exception){ } Application.Current.Shutdown();
子进程中打开 子进程打开进行更新的时候一定要等待一下,在关闭程序,否则会导致更新程序还没打开就退出了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 try { using (Process process = new Process()) { process.StartInfo.FileName = filepath; process.StartInfo.CreateNoWindow = true ; process.StartInfo.UseShellExecute = false ; process.Start(); } } catch (Exception){ } await Task.Delay(300 );Dispatcher.Invoke(() => { Close(); Application.Current.Shutdown(); });
获取错误码 如果我们需要获取进程的 ExitCode,应该使用 Process.WaitForExit() 方法来等待进程结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static bool LoadDll (string dllPath ) { int exitCode; ProcessStartInfo processStartInfo = new ProcessStartInfo { FileName = "regsvr32.exe" , Arguments = "/s " + dllPath }; using (Process process = new Process()) { process.StartInfo = processStartInfo; process.Start(); bool exited = process.WaitForExit(5000 ); if (!exited) { process.Kill(); } exitCode = process.ExitCode; } return exitCode == 0 ; }
如果要调用Close,并且要获取 ExitCode,怎么办呢?
要确保 WaitForExit 方法在调用 Close 方法之后能够正确工作,你可以在调用 Close 方法之前先设置 Process 对象的 EnableRaisingEvents 属性为 true,并订阅 Process 对象的 Exited 事件。然后,在事件处理程序中调用 WaitForExit 方法以等待进程退出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Process myProcess = new Process(); myProcess.StartInfo.FileName = "your_process.exe" ; myProcess.EnableRaisingEvents = true ; myProcess.Exited += (sender, e) => { myProcess.WaitForExit(); int exitCode = myProcess.ExitCode; }; myProcess.Start(); myProcess.Close();
执行程序的几种方式 示例
1 2 3 4 5 6 7 8 9 10 11 using (Process process = new Process()){ process.StartInfo.FileName = filepath; process.StartInfo.CreateNoWindow = true ; process.StartInfo.UseShellExecute = false ; process.StartInfo.RedirectStandardOutput = false ; process.StartInfo.RedirectStandardError = false ; process.StartInfo.RedirectStandardInput = false ; process.Start(); process.WaitForExit(); }
参数说明:
CreateNoWindow
CreateNoWindow为true时不显示窗口。
UseShellExecute
程序中大多情况都是false。
当UseShellExecute为 true 时,可以使用文件关联来启动应用程序。
例如,直接打开文本文件或图像文件,系统会使用默认关联的程序打开相应的文件。
如果要指定工作目录、传递命令行参数、重定向标准输入输出等。
需要将UseShellExecute设置为 false,这样,Process类将直接创建一个新进程来执行指定的应用程序,而不依赖操作系统的 Shell。
RedirectStandardInput
默认为false。
重定向输入,需要在执行程序的时候进行输入的时候设置为true。
RedirectStandardOutput
默认为false。
重定向标准输出,需要获取标准输出的时候设置为true。
RedirectStandardError
默认为false。
重定向标准错误,需要获取标准错误的时候设置为true。
不重定向 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public static bool GenerateThumbnails( string videoPath, string imagePath, int width = 1280 , int height = 720 ) { if (File.Exists(imagePath)) { File.Delete(imagePath); } string ffmpegpath = FfmpegPath; string whStr = "" ; if (width > 0 ) { whStr = " -s " + width + "x" + height; } try { string paras = "-i \"" + videoPath + "\" -ss 1 -vframes 1 -r 1 -ac 1 -ab 2" + whStr + " -f image2 \"" + imagePath + "\"" ; using (Process process = new Process()) { process.StartInfo.FileName = ffmpegpath; process.StartInfo.Arguments = paras; process.StartInfo.UseShellExecute = false ; process.StartInfo.RedirectStandardError = false ; process.StartInfo.CreateNoWindow = true ; process.StartInfo.RedirectStandardInput = false ; process.Start(); bool exited = process.WaitForExit(5000 ); if (!exited) { process.Kill(); } } return true ; } catch (Exception) { return false ; } }
这里不用输出的结果,所以都不重定向。
重定向标准输出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private static void AddFirewallRule ( ) { try { Process process = new Process(); string appPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); process.StartInfo.FileName = $@"{appPath} \addfirewall.bat" ; process.StartInfo.UseShellExecute = false ; process.StartInfo.CreateNoWindow = true ; process.StartInfo.RedirectStandardOutput = true ; process.StartInfo.Verb = "runas" ; process.Start(); process.WaitForExit(5000 ); string output = process.StandardOutput.ReadToEnd(); Console.WriteLine(@"================================" ); Console.WriteLine(@"添加防火墙规则:" ); Console.WriteLine(output); Console.ReadLine(); Console.WriteLine(@"================================" ); } catch (Exception ex) { ZLogHelper.Logerror.Error("添加防火墙失败" , ex); } }
重定向标准错误 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public static string GetVideoDuration (string sourceFile ) { string ffmpegpath = FfmpegPath; string duration = "" ; using (Process process = new Process()) { process.StartInfo.UseShellExecute = false ; process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; process.StartInfo.RedirectStandardError = true ; process.StartInfo.FileName = ffmpegpath; process.StartInfo.Arguments = "-i \"" + sourceFile + "\"" ; process.StartInfo.CreateNoWindow = true ; process.Start(); bool exited = process.WaitForExit(5000 ); if (!exited) { process.Kill(); } string result = process.StandardError.ReadToEnd(); if (result.Contains("Duration: " )) { duration = result.Substring(result.IndexOf("Duration: " , StringComparison.Ordinal) + ("Duration: " ).Length, ("00:00:00" ).Length); } } return duration; }
这里重定向错误,因为时长信息在错误中。
重定向输入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 _recordProcess = new Process { StartInfo = { FileName = FfmpegPath, Arguments = $"{cmdStr} " , CreateNoWindow = true , UseShellExecute = false , RedirectStandardInput = true , RedirectStandardOutput = false , RedirectStandardError = false } }; _recordProcess?.Start();
结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 if (_recordProcess != null ){ _recordProcess.StandardInput.WriteLine("q" ); bool exited = _recordProcess.WaitForExit(5000 ); if (!exited) { _recordProcess?.Kill(); _recordProcess?.Dispose(); _recordProcess = null ; } else { _recordProcess?.Dispose(); } }
退出方法对比 相关方法 对于Process的方法WaitForExit、Close、Kill、Dispose的作用
WaitForExit(): 等待关联进程退出为止。
Kill(): 结束关联进程,不会执行任何清理工作。
Close(): 关闭与进程的连接,但不会终止关联进程。调用Close()后,Process对象不再能够访问或控制该进程。
Dispose(): 释放与Process关联的资源。不会结束关联进程。
通常情况下,你不需要显式调用 Close() 或 Dispose() 方法。
当你调用 Process 类的 WaitForExit() 方法等待进程完成时,会自动释放进程相关的资源。
在进程完成后,相关的资源会被自动释放。
然而,如果你在代码中创建了很多进程对象,并且你希望在使用完之后尽快释放相关资源,可以考虑在适当的时机手动调用 Dispose() 方法。
例如,在一个处理大量进程的循环中,你可以在每次迭代结束后调用 Dispose() 方法来释放资源。
总而言之,大多数情况下不需要显式调用 Close() 方法,而是依靠自动资源释放机制。
如果需要手动释放资源,应该使用 Dispose() 方法。
注意
WaitForExit及Kill都能使进程关闭,关闭后要调用Dispose释放资源。
Close()和Dispose()并不能关闭关联的进程。
WaitForExit后不用调用Close。
常用的方式 正常退出并释放资源 1 2 3 process?.WaitForExit(); process?.Dispose(); process = null ;
等待退出并释放资源 注意
使用WaitForExit的时候建议添加超时时间,返回值为超时的时候进程是否退出了,如果没有退出我们就要杀掉进程。
设置超时时间后,一定要根据返回值处理进程,否则可能导致进程不退出的情况!
设置超时时间后,一定要根据返回值处理进程,否则可能导致进程不退出的情况!
设置超时时间后,一定要根据返回值处理进程,否则可能导致进程不退出的情况!
示例
1 2 3 4 5 6 7 8 9 10 11 12 bool exited = process.WaitForExit(5000 );if (!exited){ process.Kill(); } else { Console.WriteLine("进程已正常退出" ); } process?.Dispose(); process = null ;
强制杀进程并释放资源 1 2 3 process?.Kill(); process?.Dispose(); process=null ;
结束关联并释放资源 这种方式并不能结束关联进程
1 2 3 process?.Close(); process?.Dispose(); process = null ;
注意
在调用Close()或Kill()之后,强烈建议对关联的Process对象调用Dispose()方法,以释放相关资源。
也可以使用using自动Dispose()
1 2 3 4 5 6 using (Process process = new Process()){ process.StartInfo = processStartInfo; process.Start(); process.WaitForExit(5000 ); }
Close和Dispose Close()和Dispose()方法都可用于关闭进程。
Close()方法是Process类的一个实例方法,用于关闭关联的进程和释放与之关联的资源,包括释放由进程打开的内部句柄和操作系统资源。
调用Close()方法后,进程将被关闭,并且不再可用。
Dispose()方法是Process类的一个公开实现的IDisposable接口方法,用于释放进程及其关联资源。调用Dispose()方法将会关闭关联的进程,并释放与之关联的资源,包括通过进程打开的任何文件、管道或其他资源。使用Dispose()方法可以确保资源被立即释放,而不等待垃圾回收器的回收。
区别在于,
Close()方法主要用于关闭进程,释放资源的工作将由垃圾回收器完成。
而Dispose()方法会立即关闭进程,并手动释放与之关联的资源。