如何根据c#/ wpf应用程序中外部进程退出的原因来区分System.Diagnostic.Process.Exit事件

时间:2018-12-02 14:41:23

标签: c# wpf process

在我的应用程序中,用户可以通过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查看器已经由与第一个{{ 1}}文件。

在这种情况下,我需要以不同的方式处理事情。据我所知,我必须做到这一点:

  • 将每个新启动的进程强制进入其自己的窗口:我不 除了不知道之外,真的认为这是一个好主意 如何使用所有不同的软件类型来实现这一目标。
  • 当清楚地打开文件时,通知用户正在发生什么以及为什么相应项目中有0个实例。我更喜欢这种方法,我的方法是从ProcessModel方法的.pdf参数中获取信息。

所以我的问题是:由于所描述的情况,我该如何区分进程退出是否发生?

编辑:我当前的方法是跟踪进程ID,但是我想知道是否有更好的解决方案,也许已经实现到Process-Class

EDIT2:For better understanding, the complete project can be found here.

2 个答案:

答案 0 :(得分:4)

您正在看到单个实例进程的行为。此类程序的标准示例是任何Microsoft Office应用,IE,Adobe Reader之类的浏览器都可能与此问题相关。这些是非常大的进程,会占用大量系统资源,因此多次启动它们可能会使计算机崩溃。无论如何,在过去的日子里。

所有机制的基本机制都相同。当您启动第二个实例时,它会发现该程序已经在运行。它使用进程互操作机制(如命名管道)将命令行参数传递给第一个实例。在这种情况下,您要打开的文件的路径。第一个实例创建另一个窗口,以使用其自己的任务栏按钮显示文件的内容。与多次运行的进程几乎没有区别,除了启动后,您将看到Exited事件迅速触发。确切地说,要花费多长时间是无法预测的,通常在不到一秒钟的时间内,但是在加载计算机时可能需要几秒钟。

不是唯一的怪癖,Process.Start()甚至可能返回null。换句话说,根本没有创建任何进程。我只知道Explorer.exe的行为方式。它的api创建进程(后台执行ShellExecuteEx())的副作用,并且它认识到要求它自己启动。

值得注意的是,.NETFramework中直接支持实现此类过程。有点晦涩难懂的是,namespace直到最近还不太受欢迎。

您对此无能为力。没有标准的机制可以强制程序不执行此操作,它可能具有命令行选项,但这特定于您启动的程序。您也无法在Process对象中看到任何东西,它看起来像是一个完全正常的进程终止,并且ExitCode属性为0。除了退出速度比正常快得多之外,这是唯一的提示。复杂的是,该过程可能已发生故障,尽管在发生这种情况时ExitCode应该不为零。看到第一个打开文档的过程需要UI Automation,但是要使其通用起来可能并不容易。在this post中查找示例代码。

答案 1 :(得分:0)

我将添加一个从持久性加载的List<string> SingleInstance(请参阅稍后)。

如果processExtension在列表中,请在新窗口中启动。

如果由于这个原因进程失败(在启动.5s内启动),请将processExtension添加到列表中,然后在单独的窗口中重新启动。将列表保存到注册表或文件中,以便在启动时重新加载。

对于无法打开多个文档的进程,此过程仅启动新窗口。 如果需要,可以添加dictionary<string,int>来计算现有实例,以允许在进程中加载​​第一个文件,并故障转移到外部进程。