将当前应用程序作为“单实例"运行,并显示前一个实例 [英] Run the current application as Single Instance and show the previous instance

查看:92
本文介绍了将当前应用程序作为“单实例"运行,并显示前一个实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚实现了保护应用程序单实例的代码,以防止两次运行该应用程序.

I just implemented this code that is guarding the Single Instance of the Application, in order to not run the application twice.

现在,我想知道如何显示已经运行的原始Application进程.

Now I am wondering how I can show the original Application process that is already running.

这是我在程序类中的代码:

Here is my code in the program class:

static class Program
{
    [STAThread]
    static void Main()
    {
        const string appName = "MyappName";
        bool createdNew;
        mutex = new Mutex(true, appName, out createdNew);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Form form = new Form1();

        if (!createdNew)
        {
            form.Show();  <<=========================== NOT WORKING
            form.Visible = true; <<===================== None
            form.TopMost = true; <<===================== of
            form.BringToFront(); <<===================== these working!
            form.WindowState = FormWindowState.Maximized;
            return;
        }
        Application.Run(form);
    }        private static Mutex mutex = null;
}

推荐答案

我建议您使用 UIAutomation

I propose you a different method, using a combination of the System.Threading.Mutex class and UIAutomation AutomationElement class.

如您所知,Mutex可以是一个简单的字符串.您可以以GUID的形式为应用程序分配Mutex,但也可以是其他任何内容.
假设这是当前的应用程序Mutex:

A Mutex can be, as you already know, a simple string. You can assign an application a Mutex in the form of a GUID, but it can be anything else.
Let's assume this is the current Application Mutex:

string ApplicationMutex = "BcFFcd23-3456-6543-Fc44abcd1234";
//Or
string ApplicationMutex = "Global\BcFFcd23-3456-6543-Fc44abcd1234";

注意:
使用 "Global\" 前缀定义互斥锁的范围.如果未指定前缀,则假定并使用 "Local\" 前缀.当多个桌面处于活动状态或服务器上正在运行终端服务时,这将阻止该过程的单个实例.

Note:
Use the "Global\" Prefix to define the scope of the Mutex. If no prefix is specified, the "Local\" prefix is assumed and used instead. This will prevent a single instance of the process when multiple desktops are active or Terminal Services is running on the server.

如果我们要验证另一个正在运行的进程是否已经注册了相同的Mutex,则尝试注册我们的Mutex,如果失败,则表明我们的应用程序的另一个实例已经在运行.
我们让用户知道该应用程序仅支持单个实例,然后切换到正在运行的进程,显示其界面,最后退出重复的应用程序,处置Mutex.

If we want to verify whether another running Process has already registered the same Mutex, we try to register our Mutex and if it fails, another instance of our Application is already running.
We let the user know that the Application supports only a single instance, then switch to the running process, showing its interface and finally exit the duplicate Application, disposing of the Mutex.

根据应用程序的类型,激活应用程序先前实例的方法可能会有所不同,但是只有一些细节会发生变化.
我们可以使用

The method to activate a previous instance of the Application may vary based on the type of the Application, but only some details change.
We can use Process..GetProcesses() to retrieve a list of the running processes and verify if one of them has the same details as ours.

在这里,您有一个窗口化的应用程序(具有UI),因此可以过滤列表,排除那些没有

Here, you have a windowed Application (it has an UI), so it's already possible to filter the list, excluding those processes that do not have a MainWindowHandle.

Process[] windowedProcesses = 
    Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();

要确定正确的方法,我们可以测试

To identify the right one, we could test if the Process.ProcessName is the same.
But this name is tied to the executable name. If the file name changes (someone changes it for some reason), we will never identify the Process this way.

确定正确流程的一种可能方法是测试

One possible way to identify the right Process is to test the Process.MainModule.FileVersionInfo.ProductName and check whether it's the same.

找到后,可以使用标识的进程的MainWindowHandle创建的AutomationElement将原始应用程序放在前面.
AutomationElement可以自动执行不同的 Patterns (为UI元素提供自动化功能的各种控件).
一个

When found, it's possible to bring the original Application to front with an AutomationElement created using the MainWindowHandle of the identified Process.
The AutomationElement can automate different Patterns (sort of controls that provide automation functionalities for UI elements).
A WindowPattern allows to control a window-base control (the Platform is irrelevant, could be a WinForms' Form or a WPF's Window).

AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
WindowPattern wPattern = element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
wPattern.SetWindowVisualState(WindowVisualState.Normal);

要使用UIAutomation功能,必须在项目中添加以下引用:
-UIAutomationClient
-UIAutomationTypes

To use the UIAutomation functionalities, you have to add these refereneces in your Project:
- UIAutomationClient
- UIAutomationTypes

更新:
由于可能隐藏了应用程序的窗体,因此Process.GetProcesses()找不到它的窗口句柄,因此AutomationElement.FromHandle()不能用于标识Form窗口.

UPDATE:
Since the Application's Form might be hidden, Process.GetProcesses() will not find it's Window handle, thus AutomationElement.FromHandle() cannot be used to identify the Form Window.

可能的解决方法是使用

A possible workaround, without dismissing the UIAutomation "pattern", is to register an Automation event, using Automation.AddAutomationEventHandler, which allows to receive a notification when an UI Automation events occurs, such as a new Window is about to be shown (a Program is run).

仅当应用程序需要作为单个实例运行时,才注册事件.引发事件时,会将新的进程AutomationElement名称(Windows标题文本)与当前名称进行比较,如果相同,则隐藏的窗体将取消隐藏并以正常"状态显示.
作为故障安全措施,我们提供了一个信息MessageBox. MessageBox标题与应用程序MainForm具有相同的标题.
(使用其WindowsState设置为Minimized并且其Visible属性设置为false 的窗体进行了测试).

The event is registerd only if the Application needs to run as Single Instance. When the event is raised, the new Process AutomationElement Name (the Windows Title Text) is compared to the current and, if it's the same, the hidden Form will un-hide and show itself in Normal state.
As a fail-safe measure, we present an information MessageBox. The MessageBox caption has the same caption as the Application MainForm.
(Tested with a Form with its WindowsState set to Minimized and its Visible property set to false).

在开始原始流程之后,我们只是需要关闭当前线程并释放我们创建的资源(在这种情况下,主要是Mutex).

After the orginal Process has been brought to front, we just neeed to close the current thread and release the resources we created (mainly the Mutex, in this case).

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;

static class Program
{
    static Mutex mutex = null;

    [STAThread]
    static void Main()
    {
        Application.ThreadExit += ThreadOnExit;
        string applicationMutex = @"Global\BcFFcd23-3456-6543-Fc44abcd1234";
        mutex = new Mutex(true, applicationMutex);
        bool singleInstance = mutex.WaitOne(0, false);
        if (!singleInstance)
        {
            string appProductName = Process.GetCurrentProcess().MainModule.FileVersionInfo.ProductName;
            Process[] windowedProcesses = 
                Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();

            foreach (Process process in windowedProcesses.Where(p => p.MainModule.FileVersionInfo.ProductName == appProductName))
            {
                if (process.Id != Process.GetCurrentProcess().Id)
                {
                    AutomationElement wElement = AutomationElement.FromHandle(process.MainWindowHandle);
                    if (wElement.Current.IsOffscreen)
                    {
                        WindowPattern wPattern = wElement.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
                        #if DEBUG
                        WindowInteractionState state = wPattern.Current.WindowInteractionState;
                        Debug.Assert(!(state == WindowInteractionState.NotResponding), "The application is not responding");
                        Debug.Assert(!(state == WindowInteractionState.BlockedByModalWindow), "Main Window blocked by a Modal Window");
                        #endif
                        wPattern.SetWindowVisualState(WindowVisualState.Normal);
                        break;
                    }
                }
            }
            Thread.Sleep(200);
            MessageBox.Show("Application already running", "MyApplicationName",
                            MessageBoxButtons.OK, MessageBoxIcon.Information, 
                            MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
        }

        if (SingleInstance) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyAppMainForm());
        }
        else {
            Application.ExitThread();
        }
    }
    private static void ThreadOnExit(object s, EventArgs e)
    {
        mutex.Dispose();
        Application.ThreadExit -= ThreadOnExit;
        Application.Exit();
    }
}

在应用程序MainForm构造函数中:
(用于在运行新实例时隐藏应用程序主窗口的情况,因此Program.cs中的过程找不到其句柄)

In the Application MainForm constructor:
(this is used in case the Application's Main Window is hidden when a new instance is run, hence the procedure in Program.cs cannot find its handle)

public partial class MyAppMainForm : Form
{
    public MyAppMainForm()
    {
        InitializeComponent();
        Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, 
                                             AutomationElement.RootElement, 
                                             TreeScope.Subtree, (uiElm, evt) =>
        {
            AutomationElement element = uiElm as AutomationElement;
            string windowText = element.Current.Name;
            if (element.Current.ProcessId != Process.GetCurrentProcess().Id && windowText == this.Text)
            {
                this.BeginInvoke(new MethodInvoker(() =>
                {
                    this.WindowState = FormWindowState.Normal;
                    this.Show();
                }));
            }
        });
    }    
}

这篇关于将当前应用程序作为“单实例"运行,并显示前一个实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆