3

我正在编写将启动许多子进程的 ac# 程序。稍后,我将需要按 ID 检索这些进程,然后将这些进程与存储在 Dictionary 中的一组进程进行匹配,这些进程在首次创建时添加到 Dictionary 中。然而,我遇到了一个看起来很荒谬的问题......

Process notepad = new Process();
notepad.StartInfo.FileName = "notepad";
notepad.Start();

Process n2 = Process.GetProcessById(notepad.Id);

Debug.WriteLine(notepad == n2);       //'False', but Why isn't this true???
Debug.WriteLine(notepad.Id == n2.Id); //'True'

我使用 .NET Reflector 发现 GetProcessById 返回一个“新进程(...)”,但它似乎应该只找到对已运行进程的引用并返回它。

您可以假设第一个 Debug 语句本质上是一个调用,例如

MyCustomDataType 数据 = myDictionary[notepad];

我希望得到我最初插入的数据,而不是得到一个 KeyNotFoundException 可能是因为默认比较器正在进行参考检查。为了解决这个问题,我在字典中添加了一个自定义 IComparer,它只检查两个 Process 对象是否具有相同的 ID,因此我可以按预期获取相关数据。但是,这有其自身的问题,即未运行的进程没有进程 ID,因此有时在我的自定义 IComparer 中对 Process.ID 的调用会引发 InvalidOperationException!!!所以,我已经解决了一个问题,只是为了创建另一个问题。

所以,我想我有两个问题:

  • 为什么 .NET 不只返回对已经运行的 Process 实例的引用?
  • 我可以做些什么来匹配存储在我的字典中的进程,因为使用进程 ID 在 Process 对象的生命周期内并不总是有效?
4

5 回答 5

5

由于GetProcessByIDMSDN 文档明确指出:

创建一个新的流程组件,并将其与您指定的现有流程资源相关联。

框架无法将您拥有的相同实例返回给您。为了能够做到这一点,框架必须保留对曾经创建的所有 Process 实例的引用,并比较它们的进程 ID 以找出您已经拥有的那个。我将留给您想象这将对框架的内存和性能产生的影响。

此外,Process 类是OpenProcessWin32 API 返回的进程句柄的包装器。并且同一进程可以有多个句柄。并且输入/输出重定向可以在每个句柄的基础上完成,因此,预计场景能够有两个 Process 实例代表相同的进程,其中一个处理输出和错误流,另一个处理输入流。

另外,在我看来,您正在使用 Process 对象本身作为 Dictionary 的键。但是 Process 对象并不代表实际进程的身份,它只是进程的一种表示。您应该使用代表进程身份的东西作为键。

于 2009-12-17T06:27:54.850 回答
2

在这种情况下,您希望 .net 做什么?

第一个可执行文件

Process notepad = new Process();
notepad.StartInfo.FileName = "notepad";
notepad.Start();

假设您知道记事本实例的 processId。
在第二个可执行文件中,您可能希望使用 Id 获取该进程

第二个可执行文件

Process n2 = Process.GetProcessById(notepadIdInputByUser);

对象引用比较可以在应用程序中完成,前提是对象构造在您手中(相对于操作系统)。

.net 将如何让您将 Process 实例从第一个可执行文件转换为第二个可执行文件?

编辑:虽然Process是一个类,但它不能被引用比较。
它的作用类似于struct,您可以在其中进行成员比较。

于 2009-12-17T06:12:03.490 回答
2

1)您的第一个问题类似于:

var a = new List<string>{"one", "two", "three"};
var b = new List<string>{"one", "two", "three"};

if(a == b) { Debug.WriteLine("awesome"); } // not going to happen

没有什么可以跟踪 createdList<string>的,这与 created 的Process相同

2)我建议您不要存储Process在字典中,而是创建另一个类来存储进程 ID 和引用,并在引用的进程不再运行Process时执行一些智能操作。Process

于 2009-12-17T06:16:13.643 回答
2

正在运行的程序是操作系统进程,而不是 Process 托管类型的实例。该Process类型是围绕操作系统概念的一种托管包装对象。它允许通过隐藏 P/Invoke 调用来控制目标进程。操作系统不需要 Process 类的特定实例执行这些操作,这就是为什么GetProcessById可以返回一个新实例并仍然期望一切正常的原因。

ID 在进程本身的整个生命周期内都有效。也许您可以将EnableRaisingEvents设置为 true 并向Process.Exited事件添加一个事件处理程序,以从缓存中删除该进程?

于 2009-12-17T06:22:17.027 回答
0

使用 ProcessId 作为字典的键。

不要害怕在每次需要时使用 GetProcessById 创建一个新的 Process 实例。这是一个轻量级的对象,垃圾收集器会为你清理。

于 2009-12-17T07:19:27.167 回答