.NET 挫折 - Process.GetProcessById 返回新引用

发布于 2024-08-14 15:28:52 字数 996 浏览 13 评论 0原文

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

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 返回一个“new Process(...)”,但似乎它应该只找到一个引用已经运行的进程并返回它。

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

MyCustomDataType data = myDictionary[notepad];

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

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

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

I'm writing a c# program that will launch many child processes. At some time later on, I'll need to retrieve those processes by ID and then match those processes to a set of processes stored in a Dictionary that were added to the Dictionary when they were first created. However, I'm running into a problem that seems like pure ridiculousness...

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'

I've used .NET Reflector to find out that GetProcessById returns a 'new Process(...)', but it seems like it should just find a reference to the already running process and return it instead.

You can assume the first Debug statement is essentially a call like

MyCustomDataType data = myDictionary[notepad];

I would expect to get the data I originally inserted, instead I get a KeyNotFoundException probably because the default comparer is doing a reference check. To counter this, I've added a custom IComparer on my dictionary that just checks that the two Process objects have the same ID, so I can get the associated data as expected. However, this has its own problem in that Processes that are not running do not have process IDs, so sometimes the call in my custom IComparer to Process.ID throws an InvalidOperationException!!! So, I've fixed one problem only to create another.

So, I guess I have two questions:

  • Why doesn't .NET just return a reference to an already running Process instance?
  • What can I do to match processes stored in my dictionary since using process ID is not always valid for the lifetime of the Process object?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

抹茶夏天i‖ 2024-08-21 15:28:52

不太确定为什么必须求助于 Reflector,因为 GetProcessByID MSDN 文档明确指出:

创建一个新的 Process 组件,并且
将其与现有的
您指定的进程资源。

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

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

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

Not really sure why you had to resort to Reflector, since the GetProcessByID MSDN documentation clearly states:

Creates a new Process component, and
associates it with the existing
process resource that you specify.

The framework can't return to you the same instance you have. To be able to do this, the framework would have to keep a reference to all Process instances ever created and compare the process ID for them to find out the one you already have. I'll leave to you to imagine the implications this would have on the memory and the perf of the framework.

Besides, the Process class is a wrapper around the process handle returned by the OpenProcess Win32 API. And it is possible to have multiple handles to the same process. And input/output redirection can be done on a per-handle basis, thus, it is expected scenario to be able to have two Process instances that represent the same process and one of them dealing with the output and error streams and the other one with the input stream.

On a separate note, it seems to me that you are using the Process object itself as the key for the Dictionary. But the Process object does not represent the identity of the actual process, it is just a representation of the process. You should use as a key something that represents the identity of the process instead.

两仪 2024-08-21 15:28:52

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

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

Running programs are operating system processes, not instances of the Process managed type. The Process type is one type of managed wrapper object around an operating system concept. It allows control of the target process by hiding the P/Invoke calls. The operating system does not require a particular instance of the Process class perform these operations, which is why GetProcessById can return a new instance and still expect everything to work.

The ID is valid for the life of the process itself. Perhaps you could set EnableRaisingEvents to true and add an event handler to the Process.Exited event that removes the process from your cache?

深海蓝天 2024-08-21 15:28:52

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

没有任何东西可以跟踪创建的List's,这与创建的Process's相同

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

1) Your first question is analogous to:

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

Nothing is keeping track of created List<string>'s which is the same for created Process's

2) I suggest you don't store Process in the dictionary and instead create another class that stores the process id and the Process references and does something intelligent when the process that the Process refers to isn't running anymore.

南街女流氓 2024-08-21 15:28:52

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

第一个可执行文件

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

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

第二个可执行

Process n2 = Process.GetProcessById(notepadIdInputByUser);

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

.net 将如何将 Process 实例从第一个可执行文件获取到第二个可执行文件?

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

What would you want .net to do, in this case?

1st Executable

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

Assume that you know the processId of the notepad instance.
In the 2nd executable, you would want to get hold of that process using the Id

2nd Executable

Process n2 = Process.GetProcessById(notepadIdInputByUser);

Object reference comparison can be done within an application, provided the object construction is in your hand (as against the operating system).

How will .net get you the instance of Process from the 1st Executable into the 2nd Exectuable?

EDIT: ALthough Process is a class, it cannot be reference compared.
It acts like a struct, where you could do member comparison.

始于初秋 2024-08-21 15:28:52

使用 ProcessId 作为字典的键。

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

Use ProcessId as key of your dictionary.

Don't be afraid to create a new instance of Process using GetProcessById every time you need it. It's a lightweight object and garbage collector will clean up for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文