如何判断一个进程是否在运行?

194

当我获取到一个 System.Diagnostics.Process 的引用时,如何知道进程当前是否正在运行?

14个回答

309
这是使用名称进行操作的一种方法:
Process[] pname = Process.GetProcessesByName("notepad");
if (pname.Length == 0)
  MessageBox.Show("nothing");
else
  MessageBox.Show("run");

你可以循环所有进程来获取ID以供后续操作:
Process[] processlist = Process.GetProcesses();
foreach(Process theprocess in processlist){
   Console.WriteLine("Process: {0} ID: {1}", theprocess.ProcessName, theprocess.Id);
}

这正是我一直在寻找的。虽然这是一个非常旧的帖子,你能解释一下这是如何有效的C#吗?我并不怀疑它,我看到它可以工作,但我从来没有见过没有 { } 的if else。 - MatthewD
8
C#中长度只有一行的if/else语句不需要使用大括号来表示代码块,foreachfor语句也是如此。这归结为编码风格。 - Hallmanac
1
我也做了一些研究,找到了那些信息,但是我没有看到for的信息。作为多年的C# .NET开发人员,我从未见过这种风格。就像他们说的,“每天都会学到新东西”。感谢您的帖子和回复。 - MatthewD
3
@MatthewD 是的,实际上大多数语言(比如Java)都应该这样做。通常最好避免像这样的一行代码,并始终使用花括号,因为将来你可能需要添加更多语句,这时花括号已经在那里了。但对于像这样的事情,如果您确信只需要一个语句,那么这样做是可以的,并且符合语法规则。 - David Mordigal
2
如果找不到进程,请尝试删除扩展名。(例如:.exe) - DxTx
显示剩余3条评论

37

在使用反射器后,我发现这是最简单的方法。我为此创建了一个扩展方法:

public static class ProcessExtensions
{
    public static bool IsRunning(this Process process)
    {
        if (process == null) 
            throw new ArgumentNullException("process");

        try
        {
            Process.GetProcessById(process.Id);
        }
        catch (ArgumentException)
        {
            return false;
        }
        return true;
    }
}

Process.GetProcessById(processId)方法调用ProcessManager.IsProcessRunning(processId)方法,如果进程不存在,则会抛出ArgumentException异常。由于某些原因,ProcessManager类是内部的...


这是一个非常好的答案;然而,你不应该抛出参数空异常(因为无论如何都会抛出空引用异常,而且你没有对异常做任何处理)。此外,如果你没有调用Start()方法或者调用了close()方法,你将会得到一个InvalidOperationException。我发表了另一个答案来解决这两种情况。 - Aelphaeis
如果另一个进程获取了不再运行的原始进程的ID,这是否可能发生? - gil123

22

同步解决方案:

void DisplayProcessStatus(Process process)
{
    process.Refresh();  // Important


    if(process.HasExited)
    {
        Console.WriteLine("Exited.");
    }
    else
    {
        Console.WriteLine("Running.");
    } 
}

异步解决方案:

void RegisterProcessExit(Process process)
{
    // NOTE there will be a race condition with the caller here
    //   how to fix it is left as an exercise
    process.Exited += process_Exited;
}

static void process_Exited(object sender, EventArgs e)
{
   Console.WriteLine("Process has exited.");
}

7
第一种选择:我怎样才能知道这个进程是否已经开始运行? - reshefm
1
请注意,如果出现问题(例如无法检索到退出代码),HasExited 可能会抛出异常。 - undefined
请注意,如果出现问题(例如无法检索到退出代码),HasExited可能会抛出异常。 - tigrou

13

这应该是一行代码:

public static class ProcessHelpers {
    public static bool IsRunning (string name) => Process.GetProcessesByName(name).Length > 0;
}

12
reshefm的回答很不错;然而,它并没有考虑到一种情况,即进程从未开始过。
以下是他发布的修改版本。
public static bool IsRunning(this Process process)
{
    try
    {
        Process.GetProcessById(process.Id).Dispose();
    }
    catch (Exception e) when (e is ArgumentException or InvalidOperationException)
    {
        return false;
    }
    return true;
}

我移除了他的ArgumentNullException,因为实际上应该是一个空引用异常,并且系统会自动抛出它。我还考虑到了进程从未启动或使用close()方法关闭进程的情况。

个人而言,在审查日志异常时,我更希望看到一个ArgumentNullException而不是NullReferenceException,因为ArgumentNullException对出现了什么问题更加明确。 - Sean
1
@Sean 我通常会同意你的看法,但这是一个扩展方法。考虑到语法,我认为抛出空指针异常更合适,因为这样做与调用空对象的方法更一致。 - Aelphaeis
每次都会触发FirstHandledException事件处理程序。这样做会让你的日志充满垃圾信息,朋友。 - Latency

3
这取决于您需要此功能的可靠性程度。如果您想要百分之百准确地知道特定进程实例是否仍在运行并可用,那么您就需要放弃了。原因是从托管进程对象中只有两种方法可以识别进程。
第一种是进程 ID。不幸的是,进程 ID 不是唯一的,可能会被回收。搜索进程列表以查找匹配的 ID 只会告诉您有一个具有相同 ID 的进程正在运行,但这不一定是您的进程。
第二个项目是进程句柄。它与 ID 有相同的问题,但更难处理。
如果您正在寻找中等级别的可靠性,则检查当前进程列表中是否有相同 ID 的进程就足够了。

1

这个问题有很多相关的问题,其他人似乎已经部分解决:

  • 任何实例成员都无法保证线程安全。这意味着在评估对象属性的同时,快照的生存期中可能会发生竞争条件。
  • 对于不允许评估此类属性的权限,处理程序句柄将抛出ACCESS DENIED的Win32Exception异常。
  • 对于ISN'T RUNNING状态,在尝试评估其属性时也会引发ArgumentException异常。

无论其他人提到的属性是内部的还是非内部的,如果权限允许,仍然可以通过反射从它们获取信息。

var x = obj.GetType().GetProperty("Name", BindingFlags.NonPublic | BindingFlags.Instance);

你可以通过 P/Invoke 调用 Win32 代码来获取 快照,或者使用WMI但速度较慢。

HANDLE CreateToolhelp32Snapshot(
  DWORD dwFlags,
  DWORD th32ProcessID
);

另一个选项是使用OpenProcess / CloseProcess,但您仍然会遇到与之前相同的异常问题。

对于 WMI - OnNewEvent.Properties["?"]:

  • "ParentProcessID"(父进程ID)
  • "ProcessID"(进程ID)
  • "ProcessName"(进程名称)
  • "SECURITY_DESCRIPTOR"(安全描述符)
  • "SessionID"(会话ID)
  • "Sid"(安全标识符)
  • "TIME_CREATED"(创建时间)

1

Process.GetProcesses() 是一个好的选择。但是,根据进程的运行方式(例如作为服务或普通应用程序,是否具有标题栏),您可能需要使用一个或多个不同的条件来查找您的进程。


1
如果您将此方法放在循环中,它会消耗大量的 CPU 循环。我建议使用 GetProcessByName() 或 GetProcessByID()。 - Hao Nguyen

1
string process = "notepad";
if (Process.GetProcessesByName(process).Length > 0)
{
    MessageBox.Show("Working");
}
else
{
    MessageBox.Show("Not Working");
}

您还可以使用计时器来定期检查进程


3
难道不应该反过来吗?如果长度等于0,那不就意味着不起作用了吗? - Jay M
答案与Patrick Desjardins的回答相同:https://dev59.com/qXVC5IYBdhLWcg3wihqv#262291 - Rekshino
相反,长度大于0意味着找到了进程。 - Haseeb Mir
这段代码可能存在错误,(length == 0 应该显示 Not Working),但仍然能够完成任务。 - Momoro
已编辑以解决关于 Length == 0 的评论。 - Rich

0

也许(很可能)我没有正确理解问题,但您是否正在寻找HasExited属性,该属性将告诉您由Process对象表示的进程是否已退出(正常或异常)。

如果您引用的进程具有UI,则可以使用Responding属性来确定UI当前是否响应用户输入。

您还可以设置EnableRaisingEvents并处理Exited事件(异步发送),或者调用WaitForExit()如果您想要阻止。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接