.NET Frustration - Process.GetProcessById возвращает новую ссылку - PullRequest
3 голосов
/ 17 декабря 2009

Я пишу программу на c #, которая запускает много дочерних процессов. Через некоторое время мне нужно будет извлечь эти процессы по идентификатору, а затем сопоставить эти процессы с набором процессов, хранящихся в словаре, которые были добавлены в словарь при их первом создании. Однако я сталкиваюсь с проблемой, которая кажется чистой нелепостью ...

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 data = myDictionary [notepad];

Я ожидал бы получить данные, которые я первоначально вставил, вместо этого я получаю исключение KeyNotFoundException, вероятно, потому что компаратор по умолчанию выполняет проверку ссылок. Чтобы противостоять этому, я добавил в свой словарь пользовательский IComparer, который просто проверяет, имеют ли два объекта Process один и тот же идентификатор, чтобы я мог получить связанные данные, как и ожидалось. Однако это имеет свою собственную проблему в том, что процессы, которые не запущены, не имеют идентификаторов процессов, поэтому иногда вызов в моем пользовательском IComparer для Process.ID генерирует исключение InvalidOperationException !!! Итак, я исправил одну проблему только для создания другой.

Итак, я думаю, у меня есть два вопроса:

  • Почему .NET не просто возвращает ссылку на уже запущенный экземпляр процесса?
  • Что я могу сделать, чтобы соответствовать процессам, хранящимся в моем словаре, поскольку использование идентификатора процесса не всегда допустимо в течение всего времени жизни объекта Process?

Ответы [ 5 ]

5 голосов
/ 17 декабря 2009

Не совсем уверен, почему вам пришлось прибегнуть к Reflector, поскольку в документации MSDN GetProcessByID четко указано:

Создает новый компонент Process и связывает это с существующим ресурс процесса, который вы укажете.

Фреймворк не может вернуть вам тот же экземпляр, что и у вас. Чтобы сделать это, инфраструктура должна хранить ссылку на все экземпляры процесса, когда-либо созданные, и сравнивать идентификатор процесса для них, чтобы найти тот, который у вас уже есть. Я оставлю вас, чтобы представить, какое влияние это окажет на память и характеристики фреймворка.

Кроме того, класс Process является оберткой вокруг дескриптора процесса, возвращаемого OpenProcess Win32 API. И возможно иметь несколько дескрипторов одного и того же процесса. И перенаправление ввода / вывода может быть выполнено для каждого дескриптора, таким образом, ожидается, что сценарий сможет иметь два экземпляра Процесса, которые представляют один и тот же процесс, и один из них имеет дело с потоками вывода и ошибок, а другой - входной поток.

В отдельном примечании мне кажется, что вы используете сам объект Process в качестве ключа для словаря. Но объект Process не представляет личность фактического процесса, это просто представление процесса. Вместо этого вы должны использовать в качестве ключа то, что представляет собой идентификатор процесса.

2 голосов
/ 17 декабря 2009

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<string>, что то же самое для созданных Process '

2) Я предлагаю вам не хранить Process в словаре, а вместо этого создать другой класс, который хранит идентификатор процесса и ссылки Process и делает что-то интеллектуальное, когда процесс, на который ссылается Process, не является больше не работает.

2 голосов
/ 17 декабря 2009

Запущенные программы - это процессы операционной системы, а не экземпляры управляемого типа Process. Тип Process - это один тип управляемого объекта-оболочки вокруг концепции операционной системы. Это позволяет контролировать целевой процесс, скрывая вызовы P / Invoke. Операционная система не требует, чтобы конкретный экземпляр класса Process выполнял эти операции, поэтому GetProcessById может вернуть новый экземпляр и все еще ожидать, что все будет работать.

Идентификатор действителен для жизни самого процесса. Возможно, вы могли бы установить для EnableRaisingEvents значение true и добавить обработчик события Process.Exited , который удаляет процесс из вашего кэша?

2 голосов
/ 17 декабря 2009

Что бы вы хотели сделать в этом случае .net?

1-й исполняемый файл

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

Предположим, что вы знаете processId экземпляра блокнота.
Во 2-м исполняемом файле вам захочется получить этот процесс с помощью идентификатора

2-й исполняемый файл

Process n2 = Process.GetProcessById(notepadIdInputByUser);

Сравнение ссылок на объекты может быть выполнено в приложении, при условии, что конструкция объекта находится в вашей руке (в отличие от операционной системы).

Как .net доставит вам экземпляр Process из 1-го исполняемого файла во 2-й исполняемый?

РЕДАКТИРОВАТЬ: AL Хотя Process является классом, его нельзя сравнивать.
Он действует как struct, где вы можете сделать сравнение членов.

0 голосов
/ 17 декабря 2009

Используйте ProcessId в качестве ключа вашего словаря.

Не бойтесь создавать новый экземпляр Process, используя GetProcessById каждый раз, когда вам это нужно. Это легкий объект, и сборщик мусора очистит его для вас.

...