在我的应用程序中,用户可以通过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}}文件。
在这种情况下,我需要以不同的方式处理事情。据我所知,我必须做到这一点:
ProcessModel
方法的.pdf
参数中获取信息。 所以我的问题是:由于所描述的情况,我该如何区分进程退出是否发生?
编辑:我当前的方法是跟踪进程ID,但是我想知道是否有更好的解决方案,也许已经实现到Process-Class
EDIT2:For better understanding, the complete project can be found here.
答案 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>
来计算现有实例,以允许在进程中加载第一个文件,并故障转移到外部进程。