Как отличить события System.Diagnostic.Process.Exit по причине выхода из внешних процессов в приложениях c # / wpf - PullRequest
0 голосов
/ 02 декабря 2018

В моем приложении пользователь может выбирать различные файлы и исполняемые файлы с помощью OpenFileDialog.Из пути выбранного файла создается ProcessModel и добавляется к ObservableCollection.

План состоит в том, что пользователь может выбирать различные файлы и программы и добавлять их в список в приложении.Затем программное обеспечение должно открывать - а затем закрывать - выбранные файлы, как только происходит определенное событие (в этом случае жест пользователя, захваченный камерой).

ProcessModel содержит несколько свойств для разных параметров, но те, которые важны для моего вопроса, задаются в конструкторе следующим образом:

    public ProcessModel(string path)
    {
        ProcessName = Path.GetFileNameWithoutExtension(path);
        processExtension = Path.GetExtension(path);
        ProcessPath = path;
        instances = new List<Process>();
    }

Что я хочу сделать с каждым ProcessModel означает, что в случае запуска определенного события в приложении я хочу запустить связанный процесс.Кроме того, я хочу отслеживать, сколько экземпляров одних и тех же процессов было запущено, а также иметь возможность закрыть их с помощью другого события.Чтобы добиться этого, я слушаю событие Process.Exited и соответственно обрабатываю свой список экземпляров.Прежде чем перейти к реальной проблеме, вот методы, которые я использую (все в моем классе ProcessModel):

создание и запуск нового процесса:

    /// <summary>
    /// Starts a new instance of the current process
    /// </summary>
    public void StartNewInstance()
    {
        try
        {
            Process newInstance;
            if (processExtension == GenDefString.ExecutableExtension)
            {
                newInstance = new Process();
                newInstance.StartInfo.UseShellExecute = false;
                newInstance.StartInfo.FileName = ProcessPath;
                newInstance.Start();
            }
            else
            {
                newInstance = Process.Start(ProcessPath);
            }
            newInstance.EnableRaisingEvents = true;
            newInstance.Exited += OnProcessExited;
            instances.Add(newInstance);
            UpdateNrOfInstances();
        }
        catch(Exception e)
        {
            MessageBox.Show(e.Message);
        }
    }

остановка последнего экземплярав списке:

    /// <summary>
    /// stops the last instance int he list
    /// </summary>
    public void StopLastInstance()
    {
        if (instances.Count < 1) return; 
        try
        {
            var instanceToDelete = instances.Last();
            instanceToDelete.Exited -= OnProcessExited;
            instanceToDelete.CloseMainWindow();
            instanceToDelete.Close();
            instances.RemoveAt(instances.Count - 1);
            UpdateNrOfInstances();
        }
        catch(Exception e)
        {
            MessageBox.Show(e.Message);
        }
    }

метод, который прослушивает событие закрытия процесса (внешне):

    /// <summary>
    /// Trigger Status changed Event was raised
    /// </summary>
    /// <param name="source"></param>
    /// <param name="e"></param>
    public void OnProcessExited(object source, EventArgs e)
    {
        var process = source as Process;
        if (process == null) return;
        instances.Remove(process);
        UpdateNrOfInstances();
    }

Обновление количества текущих экземпляров в GUI:

    /// <summary>
    /// Sets the value for the number of instances (used for GUI update)
    /// </summary>
    private void UpdateNrOfInstances()
    {
        NrOfInstancesRunning = instances.Count;
    }

Как вы можете видеть в методе StartNewInstance(), я проверяю, относится ли расширение к исполняемому файлу или связанному с программным файлом (GenDefString.ExecutableExtension - это строка, содержащая @ ". Exe").Все работает как задумано, однако, если, например, пользователь помещает два разных файла .pdf, второй процесс немедленно прекращается, потому что мой pdf-просмотрщик уже был запущен ProcessModel, связанным с первым файлом .pdf.

В этом случае мне нужно обращаться с вещами по-другому.Насколько я понимаю, у меня есть способы сделать это:

  • Заставить каждый недавно запущенный процесс в свое собственное Окно: я не считаю это хорошей идеей в дополнение к тому, чтобы не знать, какчтобы добиться этого со всеми различными типами программного обеспечения.
  • Информирование пользователя о том, что происходит и почему в соответствующем элементе 0 экземпляров, когда файл явно открыт.Я предпочитаю это, и мой подход заключается в получении информации из аргумента source метода OnProcessExited().

Итак, мой вопрос: как я могу различить, если выход из процесса произошел из-за описанного случая?

РЕДАКТИРОВАТЬ: Мой текущий подход будет отслеживать идентификаторы процесса, однако яИнтересно, есть ли лучшее решение, может быть, уже реализованное для класса процессов

EDIT2: Для лучшего понимания полный проект можно найти здесь.

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Вы видите поведение одного экземпляра процесса.Стандартными примерами таких программ являются любые приложения Microsoft Office, браузеры, такие как IE, Adobe Reader, вероятно, будут иметь отношение к этому вопросу.Это очень большие процессы, которые потребляют много системных ресурсов, запуск их несколько раз может поставить машину на колени.В любом случае, в старину.

Основной механизм одинаков для всех них.Когда вы запускаете второй экземпляр, он обнаруживает, что программа уже запущена.Он использует механизм взаимодействия процессов, такой как именованный канал, для передачи аргументов командной строки первому экземпляру.В этом случае путь к файлу, который вы пытаетесь открыть.Первый экземпляр создает другое окно для отображения содержимого файла с собственной кнопкой на панели задач.Довольно неотличимо от процесса, запускающегося несколько раз, кроме того, что вы быстро увидите событие Exited после его запуска.То, сколько времени это займет, непредсказуемо, обычно менее чем за секунду, но может быть много секунд, когда машина загружена.

Не единственная странность, Process.Start () может даже вернуть null.Другими словами, никакой процесс не создан вообще.Я знаю только, что Explorer.exe ведет себя таким образом.Побочным эффектом его API является создание процесса (ShellExecuteEx () под капотом), и он распознает, что его просят запустить сам.

Примечательно, что реализация такого процесса напрямую поддерживается в .NETFramework.Немного неясно, что пространство имен в последнее время не очень популярно.

С этим очень мало что можно сделать.Не существует стандартного механизма, который бы заставлял программу этого не делать, он может иметь опцию командной строки, но это специфично для запускаемой вами программы.Ничего из того, что вы можете увидеть обратно в объекте Process, также выглядит как совершенно нормальное завершение процесса со свойством ExitCode, равным 0. Кроме того, что он завершился намного быстрее, чем обычно, это единственная реальная подсказка, которая у вас есть.С усложнением, что процесс мог работать со сбоями, хотя в этом случае ExitCode должен быть ненулевым.Чтобы увидеть, как открывается первый процесс, документ требует автоматизации пользовательского интерфейса, но его не так просто сделать универсальным.Найти образец кода в этом сообщении .

0 голосов
/ 05 декабря 2018

Я бы добавил List<string> SingleInstance, загруженный из персистентности (см. Позже).

Если processExtension присутствует в списке, запустите его в новом окне.

Если процесс завершится неудачей по этой причине (при запуске в течение 0,5 секунд после запуска), добавьте processExtension в список и перезапустите вотдельное окно.Сохраните список в реестр или файл для перезагрузки при запуске.

Эта процедура запускает новые окна только для процессов, которые не могут открыть несколько документов, как описано.При необходимости добавьте dictionary<string,int> для подсчета существующих экземпляров, чтобы разрешить внутрипроцессную загрузку первого файла и переключение на внешние процессы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...