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



在我的应用程序中,用户可以通过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"的字符串)。Everthing按预期工作,但如果例如用户放入两个不同的.pdf文件,第二个进程将立即终止,因为我的pdf查看器已经由与第一个CCD_ 12文件相关联的CCD_。

对于这种情况,我需要以不同的方式处理。就我所见,我必须采用以下方法:

  • 强迫每个新启动的进程进入自己的窗口:我不真的认为这是一个好主意,除了不知道如何使用所有不同的软件类型来实现这一点
  • 当文件明显打开时,通知用户发生了什么以及为什么相应项目中有0个实例。我更喜欢这个,我的方法是从OnProcessExited()方法的source参数中获取信息

所以我的问题是:我如何区分进程退出是否是因为所描述的情况?

编辑:我目前的方法是跟踪流程ID,但我想知道是否有更好的解决方案,可能已经在流程类中实现了

编辑2:为了更好地理解,完整的项目可以在这里找到。

您看到的是单个实例进程的行为。此类程序的标准示例是任何Microsoft Office应用程序、IE等浏览器、Adobe Reader都可能与此问题相关。这些是非常大的过程,消耗了大量的系统资源,多次启动它们可能会使机器瘫痪。不管怎样,在过去。

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

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

值得注意的是,.NETFramework直接支持实现这样的过程。有点模糊,命名空间最近并不太流行。

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

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

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

如果进程因此失败(在启动后0.5秒内启动),请将processExtension添加到列表中,并在单独的窗口中重新启动。将列表保存到注册表或启动时要重新加载的文件。

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

相关内容

最新更新