当前位置: 首页 > news >正文

C#进阶技巧掌握外部进程的启动与控制(一):进程基础与基本操作

在这里插入图片描述

引言

在 C# 开发中,启动和控制外部进程是一项非常实用的技能。无论是执行系统命令、调用外部应用程序,还是构建复杂的自动化工具,掌握进程控制都能让你的应用程序功能更加强大。本系列文章将深入探讨 C# 中进程管理的各个方面,从基础概念到高级应用,帮助你全面掌握这一重要技能。

你是否曾经需要在 C# 应用中执行命令行工具?或者想要监控某个程序的运行状态?也许你正在构建一个需要与其他应用程序交互的系统?无论哪种情况,本系列文章都将为你提供清晰的指导和实用的代码示例。

本系列共分为四篇文章:

  1. 进程基础与基本操作
  2. 进程输入输出重定向
  3. 异步进程控制
  4. 进程间通信与最佳实践

目录

  1. 进程基础概念

    • 什么是进程
    • Process 类介绍
    • 进程与线程的区别
  2. 基本进程操作

    • 启动进程
    • 等待进程结束
    • 终止进程
    • 获取进程信息

进程基础概念

什么是进程

进程(Process)是计算机中的一个基本概念,它是操作系统分配资源的基本单位,也是程序执行的实例。每个进程都有自己独立的内存空间、系统资源和执行环境。当你运行一个应用程序时,操作系统会为其创建一个或多个进程。

在现代操作系统中,进程具有以下特点:

  • 独立性:每个进程都有自己的地址空间,不会直接访问其他进程的内存
  • 并发性:多个进程可以并发执行,看起来像是同时运行
  • 动态性:进程在执行过程中会经历创建、运行、等待、终止等不同状态
  • 层次结构:进程可以创建子进程,形成父子关系

你是否思考过,当你双击一个应用程序图标时,操作系统背后发生了什么?实际上,操作系统会为该应用程序创建一个新的进程,分配必要的资源,然后开始执行程序代码。

Process 类介绍

在 C# 中,System.Diagnostics.Process 类是用于与本地和远程进程交互的主要工具。它提供了一组丰富的方法和属性,使开发者能够启动、监控和控制进程。

Process 类的主要功能包括:

  1. 启动和终止进程

    • Start():启动一个进程
    • Kill():强制终止一个进程
    • CloseMainWindow():向进程的主窗口发送关闭消息
    • WaitForExit():等待进程结束
  2. 获取进程信息

    • Id:获取进程的唯一标识符
    • ProcessName:获取进程的名称
    • MachineName:获取进程运行的计算机名称
    • StartTime:获取进程的启动时间
    • TotalProcessorTime:获取进程使用的总处理器时间
    • WorkingSet64:获取进程的物理内存使用量
    • Responding:判断进程的主窗口是否响应用户输入
  3. 进程输入输出控制

    • StandardInput:获取进程的标准输入流
    • StandardOutput:获取进程的标准输出流
    • StandardError:获取进程的标准错误流
  4. 进程集合操作

    • GetProcesses():获取本地计算机上的所有进程
    • GetProcessesByName():获取指定名称的所有进程
    • GetProcessById():获取指定 ID 的进程

让我们看一个简单的例子,了解如何使用 Process 类获取当前运行的进程信息:

using System;
using System.Diagnostics;class ProcessInfoExample
{static void Main(){// 获取当前计算机上的所有进程Process[] processes = Process.GetProcesses();Console.WriteLine($"当前运行的进程数量: {processes.Length}");Console.WriteLine("\nID\t进程名称\t\t内存使用(MB)");Console.WriteLine("----------------------------------------");// 显示每个进程的基本信息foreach (Process process in processes){try{// 将字节转换为兆字节double memoryUsageMB = process.WorkingSet64 / 1024.0 / 1024.0;Console.WriteLine($"{process.Id}\t{process.ProcessName}\t\t{memoryUsageMB:F2}");}catch (Exception ex){// 某些系统进程可能无法访问Console.WriteLine($"{process.Id}\t{process.ProcessName}\t\t访问受限");}}}
}

运行这段代码,你将看到当前计算机上所有运行的进程列表,包括它们的 ID、名称和内存使用情况。这是一个简单但功能强大的示例,展示了如何使用 Process 类获取系统进程信息。
在这里插入图片描述
你有没有想过如何编写一个任务管理器类似的工具?上面的代码就是一个很好的起点。

进程与线程的区别

在讨论进程控制时,理解进程与线程的区别非常重要:

特性进程线程
定义程序的执行实例,拥有独立的内存空间进程内的执行单元,共享进程的内存空间
资源占用较高,包括独立的内存空间和系统资源较低,仅包含执行必需的资源
创建开销较大较小
通信方式进程间通信(IPC)机制,如管道、套接字等直接共享内存,通过同步机制通信
切换开销较大较小
隔离性高,一个进程崩溃通常不会影响其他进程低,一个线程崩溃可能导致整个进程崩溃
C# 中的类System.Diagnostics.ProcessSystem.Threading.Thread

在实际应用中,我们通常使用进程来隔离不同的应用程序,而使用线程来实现单个应用程序内的并发执行。例如,当你需要执行一个外部程序时,你会创建一个新的进程;而当你需要在自己的应用程序中执行并行任务时,你会创建新的线程或使用任务并行库(TPL)。

你是否曾经思考过,为什么有些应用程序崩溃不会影响其他应用程序,而有些崩溃会导致整个系统不稳定?这与进程和线程的隔离性有关。

基本进程操作

掌握基本的进程操作是使用 C# 控制外部进程的基础。在这一部分,我们将学习如何启动进程、等待进程结束、终止进程以及获取进程信息。

启动进程

在 C# 中,启动一个新进程非常简单,主要有两种方式:

  1. 使用 Process.Start() 静态方法
  2. 创建 ProcessStartInfo 对象,然后使用 Process.Start(ProcessStartInfo) 方法

第一种方式适用于简单场景,而第二种方式提供了更多的配置选项。让我们看看这两种方式的示例:

方式一:使用 Process.Start() 静态方法
using System;
using System.Diagnostics;class SimpleProcessStart
{static void Main(){try{// 方式 1:直接启动一个应用程序Process.Start("notepad.exe");// 方式 2:启动应用程序并打开特定文件Process.Start("notepad.exe", "test.txt");// 方式 3:启动默认浏览器打开网址Process.Start(new ProcessStartInfo{FileName = "https://www.baidu.com",UseShellExecute = true  // ✅ 允许使用操作系统外壳});Console.WriteLine("成功启动进程!");}catch (Exception ex){Console.WriteLine($"启动进程时出错: {ex.Message}");}}
}

这种方式简单直观,但控制选项有限。对于需要更多配置的场景,我们可以使用 ProcessStartInfo 类。

在这里插入图片描述

方式二:使用 ProcessStartInfo 配置进程启动选项
using System;
using System.Diagnostics;class AdvancedProcessStart
{static void Main(){try{// 创建 ProcessStartInfo 对象ProcessStartInfo startInfo = new ProcessStartInfo{FileName = "cmd.exe",                // 要启动的程序Arguments = "/c dir",               // 命令行参数WorkingDirectory = @"C:\Windows",    // 工作目录UseShellExecute = false,            // 不使用操作系统 shell 启动CreateNoWindow = true,              // 不创建新窗口RedirectStandardOutput = true,      // 重定向标准输出RedirectStandardError = true        // 重定向标准错误};// 使用配置好的 ProcessStartInfo 启动进程using (Process process = Process.Start(startInfo)){// 读取标准输出string output = process.StandardOutput.ReadToEnd();// 读取标准错误string error = process.StandardError.ReadToEnd();// 等待进程结束process.WaitForExit();// 获取退出代码int exitCode = process.ExitCode;Console.WriteLine("命令输出:");Console.WriteLine(output);if (!string.IsNullOrEmpty(error)){Console.WriteLine("错误输出:");Console.WriteLine(error);}Console.WriteLine($"进程退出代码: {exitCode}");}}catch (Exception ex){Console.WriteLine($"启动进程时出错: {ex.Message}");}}
}

在这里插入图片描述

ProcessStartInfo 类提供了许多有用的属性,让你能够精确控制进程的启动方式:

属性描述
FileName要启动的应用程序或文档的名称
Arguments启动应用程序时传递的命令行参数
WorkingDirectory进程的工作目录
UseShellExecute是否使用操作系统 shell 启动进程
CreateNoWindow是否创建新窗口
WindowStyle如果创建窗口,指定其显示状态(正常、最小化、最大化等)
RedirectStandardInput是否重定向标准输入
RedirectStandardOutput是否重定向标准输出
RedirectStandardError是否重定向标准错误
Environment进程的环境变量集合
UserName启动进程的用户名
Password启动进程的密码
Domain启动进程的域

你是否注意到,当 UseShellExecute 设置为 false 时,我们可以使用重定向功能?这是因为直接启动进程(而不是通过 shell)时,我们可以直接访问进程的标准输入、输出和错误流。

等待进程结束

在许多情况下,我们需要等待启动的进程完成其工作。C# 提供了几种方式来实现这一点:

  1. 同步等待:使用 WaitForExit() 方法
  2. 带超时的等待:使用 WaitForExit(int milliseconds) 方法
  3. 异步等待:使用 WaitForExitAsync() 方法(.NET 5+ 支持)

让我们看一个示例,展示如何等待进程结束并处理超时情况:

using System;
using System.Diagnostics;class WaitForProcessExample
{static void Main(){try{// 创建进程启动信息ProcessStartInfo startInfo = new ProcessStartInfo{FileName = "ping",Arguments = "www.baidu.com -n 5",  // Ping 5 次UseShellExecute = false,CreateNoWindow = true,RedirectStandardOutput = true};Console.WriteLine("启动 ping 进程...");// 启动进程using (Process process = Process.Start(startInfo)){// 方式 1:无限期等待进程结束// process.WaitForExit();// 方式 2:等待进程结束,但设置超时时间(10秒)bool exited = process.WaitForExit(10000);if (exited){string output = process.StandardOutput.ReadToEnd();Console.WriteLine("进程已结束,输出:");Console.WriteLine(output);Console.WriteLine($"退出代码: {process.ExitCode}");}else{Console.WriteLine("进程未在指定时间内结束,准备强制终止...");process.Kill();Console.WriteLine("进程已被强制终止");}}}catch (Exception ex){Console.WriteLine($"发生错误: {ex.Message}");}}
}

在这里插入图片描述

在 .NET 5 及更高版本中,你还可以使用异步方式等待进程结束:

// .NET 5+ 支持
async Task WaitForProcessAsync()
{ProcessStartInfo startInfo = new ProcessStartInfo{FileName = "ping",Arguments = "www.baidu.com -n 5"};using (Process process = Process.Start(startInfo)){// 异步等待进程结束await process.WaitForExitAsync();Console.WriteLine("进程已结束");}
}

你有没有遇到过需要等待外部程序完成处理的情况?例如,等待文件压缩工具完成压缩后再继续后续操作?上面的代码示例可以帮助你实现这类需求。

终止进程

有时,我们需要主动终止一个进程,例如当它运行时间过长或者不再响应时。C# 提供了几种方式来终止进程:

  1. Kill():立即终止进程,不给进程清理资源的机会
  2. CloseMainWindow():向进程的主窗口发送关闭消息,类似于点击窗口的关闭按钮
  3. Kill(bool entireProcessTree):终止进程及其子进程(.NET 5+ 支持)

下面是一个示例,展示如何优雅地关闭进程,如果失败则强制终止:

using System;
using System.Diagnostics;
using System.Threading;class TerminateProcessExample
{static void Main(){Process process = null;try{// 启动记事本process = Process.Start("notepad.exe");if (process != null){Console.WriteLine($"已启动记事本进程,ID: {process.Id}");Console.WriteLine("等待 5 秒后尝试关闭进程...");Thread.Sleep(5000);// 方式 1:尝试优雅关闭(发送关闭消息到主窗口)Console.WriteLine("尝试优雅关闭进程...");bool closeSuccess = process.CloseMainWindow();if (closeSuccess){// 给进程一些时间来响应关闭请求bool exited = process.WaitForExit(3000);if (exited){Console.WriteLine("进程已优雅关闭");}else{Console.WriteLine("进程未能在指定时间内关闭,准备强制终止");process.Kill();Console.WriteLine("进程已被强制终止");}}else{Console.WriteLine("无法发送关闭消息,准备强制终止进程");process.Kill();Console.WriteLine("进程已被强制终止");}}}catch (Exception ex){Console.WriteLine($"操作进程时出错: {ex.Message}");// 确保进程被终止if (process != null && !process.HasExited){try{process.Kill();Console.WriteLine("进程已在异常处理中被终止");}catch{Console.WriteLine("无法终止进程");}}}finally{// 释放资源process?.Dispose();}}
}

在这里插入图片描述

在 .NET 5 及更高版本中,你可以使用 Kill(bool entireProcessTree) 方法来终止进程及其所有子进程:

// .NET 5+ 支持
process.Kill(entireProcessTree: true);

你是否曾经遇到过"僵尸进程"?这些进程在任务管理器中显示为"无响应",必须强制终止。上面的代码示例展示了如何先尝试优雅关闭进程,如果失败再强制终止,这是处理此类情况的最佳实践。

获取进程信息

除了启动和终止进程外,我们还经常需要获取进程的详细信息。C# 的 Process 类提供了丰富的属性和方法来获取这些信息。

以下是一个示例,展示如何获取特定进程的详细信息:

using System;
using System.Diagnostics;
using System.Linq;class ProcessInfoDetailedExample
{static void Main(){try{// 获取所有 msedge 进程Process[] msedgeProcesses = Process.GetProcessesByName("msedge");if (msedgeProcesses.Length == 0){Console.WriteLine("未找到 Edge进程,尝试启动一个...");Process.Start("msedge.exe", "--new-window https://www.baidu.com");// 给进程一些启动时间System.Threading.Thread.Sleep(2000);// 重新获取进程msedgeProcesses = Process.GetProcessesByName("msedge");}if (msedgeProcesses.Length > 0){// 获取第一个 msedge 进程的详细信息Process msedgeProcess = msedgeProcesses[0];Console.WriteLine("msedge 进程详细信息:");Console.WriteLine($"进程 ID: {msedgeProcess.Id}");Console.WriteLine($"进程名称: {msedgeProcess.ProcessName}");Console.WriteLine($"主窗口标题: {msedgeProcess.MainWindowTitle}");try{Console.WriteLine($"启动时间: {msedgeProcess.StartTime}");Console.WriteLine($"运行时长: {DateTime.Now - msedgeProcess.StartTime}");Console.WriteLine($"CPU 使用时间: {msedgeProcess.TotalProcessorTime}");Console.WriteLine($"物理内存使用: {msedgeProcess.WorkingSet64 / 1024 / 1024} MB");Console.WriteLine($"虚拟内存使用: {msedgeProcess.VirtualMemorySize64 / 1024 / 1024} MB");Console.WriteLine($"线程数: {msedgeProcess.Threads.Count}");Console.WriteLine($"句柄数: {msedgeProcess.HandleCount}");Console.WriteLine($"优先级: {msedgeProcess.BasePriority}");Console.WriteLine($"是否响应: {msedgeProcess.Responding}");}catch (Exception ex){Console.WriteLine($"获取某些进程信息时出错: {ex.Message}");}// 获取进程的模块(加载的 DLL)Console.WriteLine("\n加载的前 5 个模块:");try{foreach (ProcessModule module in msedgeProcess.Modules.Cast<ProcessModule>().Take(5)){Console.WriteLine($"  {module.ModuleName} - {module.FileName}");}}catch (Exception ex){Console.WriteLine($"获取模块信息时出错: {ex.Message}");}}else{Console.WriteLine("无法找到或启动 msedge 进程");}}catch (Exception ex){Console.WriteLine($"发生错误: {ex.Message}");}}
}

在这里插入图片描述

这个示例展示了如何获取进程的各种详细信息,包括资源使用情况、线程数量、加载的模块等。这些信息对于监控和调试应用程序非常有用。

总结

在本文中,我们学习了 C# 中进程控制的基础知识和基本操作。我们了解了什么是进程,以及如何使用 System.Diagnostics.Process 类来启动、等待、终止和获取进程信息。这些基础知识为我们后续学习更高级的进程控制技术奠定了基础。

在下一篇文章中,我们将深入探讨进程输入输出重定向,学习如何与外部进程进行交互,包括向进程发送输入和获取进程的输出。

http://www.dtcms.com/a/477851.html

相关文章:

  • 昂瑞微:实现精准突破,攻坚射频“卡脖子”难题
  • 延安做网站的公司电话如何用云服务器搭建个人网站
  • shellSort
  • idea一直卡在build不动(Writing class)
  • LSTM自然语言处理情感分析项目(四)整合调用各类与方法形成主程序
  • MySQL为什么选择B+tree索引作为核心索引结构?
  • 在 Windows 11 上使用 JetBrains Rider 2025.2 创建 Avalonia 项目完整指南
  • 隐私保护与数据安全合规(十)
  • 【工业场景】用YOLOv8实现人员打电话识别
  • 丽水建设网站制作几年前我为客户建设网站
  • 主线程 MainLooper 和一般 Looper 的异同?
  • 【论文精读】STAR:基于文本到视频模型的空间-时间增强真实世界视频超分
  • 建设银行的积分网站百度渠道开户
  • 万网 速成网站嘉定品牌网站建设
  • Ruby on Rails 从0 开始入门到进阶到高级 - 10分钟速通版
  • Windows Docker Desktop占用C盘空间过大解决办法集合
  • 平面的方程公式
  • 2025年“羊城杯”网络安全大赛 线上初赛 (WriteUp)
  • 网络安全概念之网闸防火墙AI版
  • 学习笔记2: 深度学习之logistic回归梯度下降
  • 网络安全等级测评师能力评估样卷及答案
  • 网站服务器用什么系统网站建设及管理制度文章
  • 网站添加wordpress创意咨询策划公司
  • 企业网站设计专业好吗胶州房产网
  • 环境变量完全指南:用 Vite 把「配置」玩出花
  • 深入解析JAVA虚拟线程
  • 不同设计牙周探针在深牙周袋探查中的精确性与局限性比较
  • 三极管分类
  • Leetcode 3710. Maximum Partition Factor
  • 亚马逊,塔吉特采购测评:高砍单率核心原因及技术破解策略