我向您建议一种不同的方法,结合使用类和UI自动化 https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.automation?view=netframework-4.7.2 班级。
A Mutex
正如您所知,可以是一个简单的字符串。您可以以 GUID 的形式为应用程序分配互斥体,但也可以是其他任何形式。
我们假设这是当前的应用程序Mutex
:
string ApplicationMutex = "BcFFcd23-3456-6543-Fc44abcd1234";
//Or
string ApplicationMutex = "Global\BcFFcd23-3456-6543-Fc44abcd1234";
Note:
使用"Global\"
定义互斥量范围的前缀。如果没有指定前缀,则"Local\"
假定并使用前缀。当多个桌面处于活动状态或终端服务在服务器上运行时,这将阻止该进程的单个实例。
如果我们想验证另一个正在运行的进程是否已经注册了相同的进程Mutex
,我们尝试注册我们的Mutex
如果失败,则我们的应用程序的另一个实例已经在运行。
我们让用户知道该应用程序仅支持单个实例,然后切换到正在运行的进程,显示其界面,最后退出重复的应用程序,处理掉Mutex
.
激活应用程序的先前实例的方法可能会根据应用程序的类型而有所不同,但只有一些细节发生变化。
我们可以用检索正在运行的进程的列表并验证其中一个进程是否具有与我们相同的详细信息。
在这里,您有一个窗口应用程序(它有一个 UI),因此已经可以过滤列表,排除那些没有窗口的进程.
Process[] windowedProcesses =
Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();
为了确定正确的,我们可以测试是一样的。
但是这个name与可执行文件名称相关。如果文件名发生变化(有人出于某种原因更改了它),我们将永远不会以这种方式识别进程。
识别正确流程的一种可能方法是测试并检查是否相同。
找到后,可以将原始应用程序置于前面AutomationElement
使用创建的MainWindowHandle
已识别的流程。
The AutomationElement
可以自动化不同的Patterns(为 UI 元素提供自动化功能的控件)。
A 允许控制基于窗口的控件(平台无关,可以是 WinForms 的窗体或 WPF 的窗口)。
AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
WindowPattern wPattern = element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
wPattern.SetWindowVisualState(WindowVisualState.Normal);
要使用UIAutomation
功能,您必须在项目中添加这些引用:
- UIAutomationClient
- UIAutomationTypes
UPDATE:
由于申请表可能被隐藏,Process.GetProcesses()
不会找到它的窗口句柄,因此AutomationElement.FromHandle()
不能用来识别Form
窗户。
一种可能的解决方法是在不放弃 UIAutomation“模式”的情况下注册一个 Automation 事件,使用,它允许在发生 UI 自动化事件时接收通知,例如即将显示新窗口(程序正在运行)。
仅当应用程序需要作为单实例运行时才会注册该事件。当事件发生时,新的进程AutomationElement
名称(Windows 标题文本)与当前名称进行比较,如果相同,则隐藏的窗体将取消隐藏并以正常状态显示。
作为一种故障安全措施,我们提供了一个信息MessageBox
. The MessageBox
标题与应用程序具有相同的标题MainForm
.
(使用表格进行测试WindowsState
set to 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.Dispose();
Application.ThreadExit -= ThreadOnExit;
Application.Exit();
}
}
在应用程序中MainForm
构造函数:
(这是在运行新实例时应用程序的主窗口被隐藏的情况下使用的,因此中的过程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();
}));
}
});
}
}