Process 类位于System.Diagnostics 命名空间下,他提供对本地和远程进程的访问,允许启动 停止 监控 和 与系统进行交互。 无论是启动一个外部程序(如记事本,命令行工具),还是获取当前正在运行的进程列表,Process都是核心工具。
1. 核心概念
-
进程(Process):正在运行的程序实例,拥有独立的内存空间、资源句柄和线程。
-
Process 类 :封装了对进程的操作,可以启动新进程 ,附加到现有进程 ,查询进程信息 ,等待进程结束 ,读写进程的标准输入/输出等。
-
命名空间 :
using System.Diagnostics;
2. 常用启动方式
2.1 简单启动(使用默认配置)
csharp
Process.Start("notepad.exe");
Process.Start("https://www.microsoft.com"); // 打开网址
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "ping", // 可执行文件或命令
Arguments = "8.8.8.8 -t", // 参数
UseShellExecute = false, // 是否使用操作系统 shell 启动(false 可重定向 IO)
RedirectStandardOutput = true, // 重定向标准输出流
CreateNoWindow = true // 不显示窗口
};
Process process = new Process { StartInfo = startInfo };
process.Start();
string output = process.StandardOutput.ReadToEnd(); // 读取全部输出
process.WaitForExit();
3. 常用方法详解
| 方法名 | 说明 |
|---|---|
| Start() | 启动进程,根据 StartInfo 配置启动新进程。静态版本 Process.Start(info) 直接启动并返回 Process 实例。 |
| Kill() | 强制终止进程。立即停止,不提供保存数据的机会。 |
| CloseMainWindow() | 友好地关闭进程的主窗口(向主窗口发送关闭消息),与用户点击关闭按钮类似。适用于 GUI 程序。 |
| WaitForExit() | 同步等待进程退出。可以指定超时时间(毫秒),返回 bool 表示是否在超时前退出。 |
| WaitForInputIdle() | 等待进程进入空闲状态,常用于图形界面进程,确保窗口已完成初始化。 |
| Refresh() | 刷新进程属性信息(如内存使用、CPU 时间等),使当前实例从操作系统获取最新数据。 |
| GetProcesses() | 静态方法,获取本地计算机上所有正在运行的进程数组。 |
| GetProcessesByName(string) | 静态方法,根据进程名称获取所有匹配的进程实例(如 "notepad")。 |
| GetProcessById(int) | 静态方法,通过进程 ID 获取指定进程。若不存在则抛出异常。 |
| Start(string, string) | 静态重载,快速启动程序并传递参数。 |
4. 常用属性
| 属性名 | 说明 |
|---|---|
| Id | 当前进程的唯一标识符(PID)。 |
| ProcessName | 进程的名称(不含路径,例如 "notepad")。 |
| MainWindowTitle | 进程主窗口的标题。 |
| MainModule | 进程的主模块信息(包含文件名等)。 |
| HasExited | 指示关联的进程是否已终止。 |
| ExitCode | 进程退出时代码(需在 HasExited 为 true 时读取)。 |
| StartTime | 进程启动的时间。 |
| TotalProcessorTime | 进程使用的总处理器时间。 |
| WorkingSet64 | 进程的物理内存使用量(字节)。 |
| PriorityClass | 进程的优先级类(可读写)。 |
| StandardInput / StandardOutput / StandardError | 用于重定向输入输出流(需先设置重定向)。 |
5. 事件处理
Process 类支持两个重要事件:
5.1 Exited 事件
在进程退出时触发。使用前必须将 EnableRaisingEvents 属性设为 true。
csharp
process.EnableRaisingEvents = true;
process.Exited += (sender, e) =>
{
Console.WriteLine("进程已退出,退出代码:" + process.ExitCode);
};
process.Start();
5.2 OutputDataReceived / ErrorDataReceived 事件
当进程通过标准输出/错误流输出行时触发。需要先设置重定向,并调用 BeginOutputReadLine() 开始异步读取。
csharp
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
process = new Process { StartInfo = startInfo };
process.OutputDataReceived += (sender, e) =>
{
if (e.Data != null) Console.WriteLine("[输出] " + e.Data);
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data != null) Console.WriteLine("[错误] " + e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
6. 完整示例:启动并实时读取输出
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/c dir C:\\",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
process.StartInfo = startInfo;
process.OutputDataReceived += (sender, e) => Console.WriteLine(e.Data);
process.ErrorDataReceived += (sender, e) => Console.WriteLine("ERR: " + e.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.WriteLine("退出码:" + process.ExitCode);
}
}
7. 获取系统中运行的进程
// 获取所有进程
Process[] allProcesses = Process.GetProcesses();
foreach (Process proc in allProcesses)
{
Console.WriteLine($"名称: {proc.ProcessName}, ID: {proc.Id}, 内存: {proc.WorkingSet64 / 1024} KB");
}
// 获取特定名称的进程
Process[] notepads = Process.GetProcessesByName("notepad");
if (notepads.Length > 0)
{
notepads[0].Kill(); // 强制关闭第一个记事本进程
}
8. 注意事项
-
资源释放 :
Process实现了IDisposable,使用完后应调用Dispose()(或使用using语句)释放系统资源。csharp
using (Process p = Process.Start("notepad.exe")) { // ... } -
权限问题:某些操作(如获取已结束进程的退出码、修改远程进程、调试特权进程)可能需要管理员权限。
-
异步读取 :如果同时使用
ReadToEnd()和事件方式,可能造成死锁。建议只采用其中一种。 -
Shell 使用 :当
UseShellExecute = true时(默认),可以启动文档、网址等关联程序,但不能重定向 IO。若要重定向,必须设为false。 -
64/32位 :从 32 位程序启动 64 位系统目录下的程序可能受文件系统重定向影响,可通过
ProcessStartInfo的FileName指定完整路径,或使用SysNative替代System32来访问原生 64 位目录。
9. 典型应用场景
-
调用外部工具:如压缩工具、转换工具、命令行编译器。
-
自动化操作:控制浏览器打开网页,模拟用户操作(需配合窗口消息或 UI 自动化)。
-
进程监控:监听程序是否崩溃,自动重启。
-
系统信息收集:获取当前运行的进程清单、CPU/内存占用。
-
管道通信:通过标准输入输出流与子进程交互(如执行命令行命令)。
10. 总结
Process 类是 C# 中与操作系统进程交互的万能钥匙。理解它的启动方式、重定向、事件和常用查询方法,能让你轻松实现许多系统级编程任务。通过合理运用 ProcessStartInfo,你可以精细控制外部进程的行为,并安全地与它们交换数据。
掌握 Process,就掌握了 .NET 进程管理的核心。
在C#中,静态Static成员不能直接访问非静态(实例)成员。
静态Static=图纸上的说明书
1. 静态(Static) = 图纸上的说明书
-
特性:它是全局唯一的,不需要造出机器,图纸上就写着"激光功率上限是 3000W"。
-
权限:它只能谈论图纸上有的东西,它不知道这台图纸以后会造出 10 台还是 100 台机器。
2. 实例(Instance) = 编号为 001 的激光机
-
特性 :只有当你按下生产线启动键(
new Form1()),这台具体的机器(对象)才存在。 -
控件(txtBox) :它是这台机器上的一个零件(触摸屏)。
静态与实例的关系表
| 成员类型 | 所属对象 | 什么时候存在 | 能否访问静态成员 | 能否访问实例成员 |
|---|---|---|---|---|
| 静态 (Static) | 类(图纸) | 程序启动即存在 | 能 | 不能(因为不知道指向哪个实例) |
| 实例 (Instance) | 对象(机器) | new 出来之后才存在 |
能 | 能 |
-
静态(static)与实例(instance)的隔离:静态方法属于"类"本身,它在程序一运行就存在了。。而实例化成员只有当窗体被创建之后才会存在。
-
控件是实例成员 :你只有当你运行程序、窗口弹出来的那一刻,这个 控件才真正被"造"出来(实例化)。
-
违逻辑访问 :静态方法 运行在"上帝视角",它不知道现在屏幕上有哪个具体的
Form窗口,所以它无法访问属于某个特定窗口的 控件。