在我的应用程序中,用户可以通过 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-viewer 已经由 ProcessModel
关联到第一个 .pdf
文件。
对于这种情况,我需要以不同的方式处理事情。据我所知,我必须这样做:
- 强制每个新启动的进程进入它自己的窗口:我不 除了真的不知道之外,真的认为这是一个好主意 如何使用所有不同的软件类型实现这一目标。
- 在清楚地打开文件时,通知用户发生了什么以及为什么相应项目中有 0 个实例。我更喜欢这个,我的方法是从
OnProcessExited()
方法的source
参数中获取信息。
所以我的问题是:我如何区分进程退出是否是由于所描述的情况而发生的?
编辑:我目前的方法是跟踪进程 ID,但是我想知道是否有更好的解决方案,也许已经在进程类中实现了
编辑 2:For better understanding, the complete project can be found here.
最佳答案
您看到的是单个实例 进程的行为。此类程序的标准示例是任何 Microsoft Office 应用程序,IE 等浏览器,Adobe Reader 可能与此问题相关。这些是消耗大量系统资源的非常大的进程,多次启动它们可能会使机器崩溃。无论如何在过去。
它们的底层机制都是相同的。当您启动第二个实例时,它发现该程序已经在运行。它使用类似于命名管道的进程互操作机制将命令行参数传递给第一个实例。在这种情况下,您尝试打开的文件的路径。第一个实例创建另一个窗口来显示文件的内容和它自己的任务栏按钮。与多次运行的进程几乎没有区别,除此之外您会看到 Exited 事件在启动后迅速触发。具体需要多长时间无法预测,通常不到一秒,但在机器加载时可能需要很多秒。
Process.Start() 并不是唯一的怪癖,它甚至可能返回 null
。换句话说,根本没有创建任何进程。我只知道 Explorer.exe 有这种行为。它的 api 创建进程的副作用(ShellExecuteEx() 在引擎盖下)并且它认识到它被要求自行启动。
值得注意的是,.NETFramework 直接支持实现这样的过程。有点晦涩,namespace最近不是很受欢迎。
对此您无能为力。没有强制程序不执行此操作的标准机制,它可能有一个命令行选项,但它特定于您启动的程序。您在 Process 对象中也看不到任何东西,它看起来像是一个完全正常的进程终止,ExitCode 属性为 0。除此之外,它比正常退出快得多,这是您拥有的唯一真实提示。复杂的是该过程可能出现故障,尽管发生这种情况时 ExitCode 应该是非零的。看到第一个进程打开文档需要 UI Automation,但可能不太容易通用。在 this post 中查找示例代码.
关于c# - c#/wpf应用中外部进程如何根据退出原因区分System.Diagnostic.Process.Exit事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53581295/