将当前应用程序作为Single Instance运行并显示上一个实例

问题描述 投票:3回答:2

我刚刚实现了这个守护应用程序单实例的代码,以便不运行应用程序两次。

现在我想知道如何显示已经运行的原始应用程序进程。

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

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;
}
c# winforms ui-automation single-instance
2个回答
4
投票

我提出了一个不同的方法,使用System.Threading.Mutex类和UIAutomation AutomationElement类的组合。

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

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

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

如果我们想看看另一个正在运行的进程是否已经注册了相同的Mutex,我们会尝试注册我们的Mutex,如果失败,我们的应用程序的另一个实例已经在运行。 我们让用户知道Application只支持单个实例,然后切换到正在运行的进程,显示其界面,最后退出重复的Application,处理Mutex

激活应用程序的先前实例的方法可能因应用程序的类型而异,但只有一些细节会发生变化。 我们可以使用Process..GetProcesses()来检索正在运行的进程列表,并验证其中一个是否具有与我们相同的详细信息。

这里有一个窗口应用程序(它有一个UI),因此已经可以过滤列表,排除那些没有MainWindowHandle的进程。

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

为了确定正确的,我们可以测试Process.ProcessName是否相同。 但是这个名称与可执行文件名相关联。如果文件名更改(某人因某种原因更改了文件名),我们将永远不会以这种方式识别进程。

确定正确流程的一种可能方法是测试Process.MainModule.FileVersionInfo.ProductName并检查它是否相同。

找到后,可以使用UIAutomation AutomationElement将原始应用程序放在前面,使用已识别过程的MainWindowHandle创建。 AutomationElement可以自动化不同的模式(为UI元素提供自动化功能的控件类型)。 WindowPattern允许控制基于窗口的控件(平台无关紧要,可以是WinForms'表单或WPF窗口)。

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

要使用qazxsw poi功能,您必须在项目中添加这些参考: - qazxsw poi - qazxsw poi

更新: 由于应用程序的表单可能被隐藏,UIAutomation将找不到它的窗口句柄,因此UIAutomationClient不能用于识别UIAutomationTypes窗口。

在不解除UIAutomation“模式”的情况下,可能的解决方法是使用Process.GetProcesses()注册自动化事件,该事件允许在UI自动化事件发生时接收通知,例如即将显示新窗口(程序运行) )。

仅当应用程序需要作为Single Istance运行时才会注册该事件。引发事件时,将新的进程AutomationElement.FromHandle()名称(Windows标题文本)与当前进行比较,如果相同,则隐藏的表单将取消隐藏并显示为正常状态。 作为一种自动防故障措施,我们提供了一个信息FormAutomation.AddAutomationEventHandler标题与应用程序AutomationElement具有相同的标题。 (用一个表格测试,其MessageBox设置为MessageBox,其MainForm属性设置为WindowsState)。


在原始Process被带到前面之后,我们只需要关闭当前线程并释放我们创建的资源(在这种情况下主要是Mutex)。

Minimized

在应用程序Visible构造函数中: (这用于在运行新实例时隐藏应用程序的主窗口,因此false中的过程无法找到其句柄)

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.Close();
        mutex.Dispose();
        Application.ThreadExit -= ThreadOnExit;
        Application.Exit();
    }
}

1
投票

只运行一次:

MainForm

ProcessUtils:

Program.cs

正常运行:

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();
                }));
            }
        });
    }    
}
© www.soinside.com 2019 - 2024. All rights reserved.