运行 当前应用程序作为单实例并显示上一个实例
Run the current application as Single Instance and show the previous instance
我刚刚实现了这段代码来保护应用程序的单个实例,以免 运行 应用程序两次。
现在我想知道如何显示已经 运行ning 的原始申请流程。
这是我在程序中的代码 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;
}
运行只有一次:
static class Program
{
[STAThread]
static void Main()
{
bool createdNew = true;
using (Mutex mutex = new Mutex(true, "samplename", out createdNew))
{
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new Form1());
}
else
{
ProcessUtils.SetFocusToPreviousInstance("samplename");
}
}
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
}
}
进程实用程序:
public static class ProcessUtils
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_RESTORE = 9;
[DllImport("user32.dll")]
static extern IntPtr GetLastActivePopup(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsWindowEnabled(IntPtr hWnd);
public static void SetFocusToPreviousInstance(string windowCaption)
{
IntPtr hWnd = FindWindow(null, windowCaption);
if (hWnd != null)
{
IntPtr hPopupWnd = GetLastActivePopup(hWnd);
if (hPopupWnd != null && IsWindowEnabled(hPopupWnd))
{
hWnd = hPopupWnd;
}
SetForegroundWindow(hWnd);
if (IsIconic(hWnd))
{
ShowWindow(hWnd, SW_RESTORE);
}
}
}
}
正常 运行 :
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
我建议你一个不同的方法,使用System.Threading.Mutex class and UIAutomation AutomationElementclass的组合。
如您所知,A Mutex
可以是一个简单的字符串。您可以为应用程序分配 GUID 形式的 Mutex,但它可以是任何其他形式。
假设这是当前应用程序 Mutex
:
string ApplicationMutex = "BcFFcd23-3456-6543-Fc44abcd1234";
//Or
string ApplicationMutex = "Global\BcFFcd23-3456-6543-Fc44abcd1234";
注:
使用 "Global\"
前缀来定义 Mutex 的范围。如果未指定前缀,则假定并使用 "Local\"
前缀。当多个桌面处于活动状态或终端服务在服务器上 运行ning 时,这将阻止进程的单个实例。
如果我们想验证另一个 运行ning 进程是否已经注册了相同的 Mutex
,我们尝试注册我们的 Mutex
,如果失败,我们的应用程序的另一个实例已经运行宁了。
我们让用户知道这个Application只支持单实例,然后切换到运行ning进程,显示它的界面,最后退出重复的Application,处理掉Mutex
。
激活应用程序先前实例的方法可能会根据应用程序的类型而有所不同,但只有一些细节会发生变化。
我们可以使用 Process..GetProcesses() 检索 运行ning 进程的列表,并验证其中一个是否与我们的具有相同的详细信息。
在这里,您有一个窗口应用程序(它有一个 UI),因此已经可以过滤列表,排除那些没有 MainWindowHandle 的进程。
Process[] windowedProcesses =
Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();
要确定正确的,我们可以测试 Process.ProcessName 是否相同。
但是这个 name 与可执行文件名称相关联。如果文件名发生变化(有人出于某种原因更改了它),我们将永远不会以这种方式识别进程。
识别正确进程的一种可能方法是测试 Process.MainModule.FileVersionInfo.ProductName 并检查它们是否相同。
找到后,可以将原始应用程序与使用已识别进程的 MainWindowHandle
创建的 AutomationElement
放在前面。
AutomationElement
可以自动化不同的 Patterns(为 UI 元素提供自动化功能的控件)。
WindowPattern 允许控制 window-base 控件(平台无关紧要,可以是 WinForms 的窗体或 WPF 的 Window)。
AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
WindowPattern wPattern = element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
wPattern.SetWindowVisualState(WindowVisualState.Normal);
To use the UIAutomation
functionalities, you have to add these refereneces in your Project:
- UIAutomationClient
- UIAutomationTypes
更新:
由于应用程序的表单可能被隐藏,Process.GetProcesses()
将找不到它的 Window 句柄,因此 AutomationElement.FromHandle()
不能用于识别 Form
Window。
一个可能的解决方法是在不关闭 UIAutomation "pattern" 的情况下使用 Automation.AddAutomationEventHandler 注册一个 Automation 事件,这样可以在 UI 自动化事件发生,例如一个新的 Window 即将显示(一个程序是 运行)。
仅当应用程序需要 运行 作为单一实例时才注册该事件。当事件发生时,新的进程 AutomationElement
名称(Windows 标题文本)与当前进程进行比较,如果相同,隐藏的表单将 un-hide 并显示在正常状态。
作为 fail-safe 措施,我们提供信息 MessageBox
。 MessageBox
标题与应用程序 MainForm
.
具有相同的标题
(使用 WindowsState
设置为 Minimized
且 Visible
属性 设置为 false
的表单进行测试。
原始进程被带到前面后,我们只需要关闭当前线程并释放我们创建的资源(在本例中主要是 Mutex)。
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
构造函数中:
(这用于在新实例为 运行 时应用程序的 Main Window 被隐藏的情况,因此 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();
}));
}
});
}
}
如果您还在寻找答案。有一个很好的例子 here
使用 windows 消息来恢复之前的实例。即使与 FindWindow 相反,第一个实例被最小化它也能工作在这种情况下不起作用。
我刚刚实现了这段代码来保护应用程序的单个实例,以免 运行 应用程序两次。
现在我想知道如何显示已经 运行ning 的原始申请流程。
这是我在程序中的代码 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;
}
运行只有一次:
static class Program
{
[STAThread]
static void Main()
{
bool createdNew = true;
using (Mutex mutex = new Mutex(true, "samplename", out createdNew))
{
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new Form1());
}
else
{
ProcessUtils.SetFocusToPreviousInstance("samplename");
}
}
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
}
}
进程实用程序:
public static class ProcessUtils
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_RESTORE = 9;
[DllImport("user32.dll")]
static extern IntPtr GetLastActivePopup(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsWindowEnabled(IntPtr hWnd);
public static void SetFocusToPreviousInstance(string windowCaption)
{
IntPtr hWnd = FindWindow(null, windowCaption);
if (hWnd != null)
{
IntPtr hPopupWnd = GetLastActivePopup(hWnd);
if (hPopupWnd != null && IsWindowEnabled(hPopupWnd))
{
hWnd = hPopupWnd;
}
SetForegroundWindow(hWnd);
if (IsIconic(hWnd))
{
ShowWindow(hWnd, SW_RESTORE);
}
}
}
}
正常 运行 :
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
我建议你一个不同的方法,使用System.Threading.Mutex class and UIAutomation AutomationElementclass的组合。
如您所知,A Mutex
可以是一个简单的字符串。您可以为应用程序分配 GUID 形式的 Mutex,但它可以是任何其他形式。
假设这是当前应用程序 Mutex
:
string ApplicationMutex = "BcFFcd23-3456-6543-Fc44abcd1234";
//Or
string ApplicationMutex = "Global\BcFFcd23-3456-6543-Fc44abcd1234";
注:
使用 "Global\"
前缀来定义 Mutex 的范围。如果未指定前缀,则假定并使用 "Local\"
前缀。当多个桌面处于活动状态或终端服务在服务器上 运行ning 时,这将阻止进程的单个实例。
如果我们想验证另一个 运行ning 进程是否已经注册了相同的 Mutex
,我们尝试注册我们的 Mutex
,如果失败,我们的应用程序的另一个实例已经运行宁了。
我们让用户知道这个Application只支持单实例,然后切换到运行ning进程,显示它的界面,最后退出重复的Application,处理掉Mutex
。
激活应用程序先前实例的方法可能会根据应用程序的类型而有所不同,但只有一些细节会发生变化。
我们可以使用 Process..GetProcesses() 检索 运行ning 进程的列表,并验证其中一个是否与我们的具有相同的详细信息。
在这里,您有一个窗口应用程序(它有一个 UI),因此已经可以过滤列表,排除那些没有 MainWindowHandle 的进程。
Process[] windowedProcesses =
Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();
要确定正确的,我们可以测试 Process.ProcessName 是否相同。
但是这个 name 与可执行文件名称相关联。如果文件名发生变化(有人出于某种原因更改了它),我们将永远不会以这种方式识别进程。
识别正确进程的一种可能方法是测试 Process.MainModule.FileVersionInfo.ProductName 并检查它们是否相同。
找到后,可以将原始应用程序与使用已识别进程的 MainWindowHandle
创建的 AutomationElement
放在前面。
AutomationElement
可以自动化不同的 Patterns(为 UI 元素提供自动化功能的控件)。
WindowPattern 允许控制 window-base 控件(平台无关紧要,可以是 WinForms 的窗体或 WPF 的 Window)。
AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
WindowPattern wPattern = element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
wPattern.SetWindowVisualState(WindowVisualState.Normal);
To use the
UIAutomation
functionalities, you have to add these refereneces in your Project:
-UIAutomationClient
-UIAutomationTypes
更新:
由于应用程序的表单可能被隐藏,Process.GetProcesses()
将找不到它的 Window 句柄,因此 AutomationElement.FromHandle()
不能用于识别 Form
Window。
一个可能的解决方法是在不关闭 UIAutomation "pattern" 的情况下使用 Automation.AddAutomationEventHandler 注册一个 Automation 事件,这样可以在 UI 自动化事件发生,例如一个新的 Window 即将显示(一个程序是 运行)。
仅当应用程序需要 运行 作为单一实例时才注册该事件。当事件发生时,新的进程 AutomationElement
名称(Windows 标题文本)与当前进程进行比较,如果相同,隐藏的表单将 un-hide 并显示在正常状态。
作为 fail-safe 措施,我们提供信息 MessageBox
。 MessageBox
标题与应用程序 MainForm
.
具有相同的标题
(使用 WindowsState
设置为 Minimized
且 Visible
属性 设置为 false
的表单进行测试。
原始进程被带到前面后,我们只需要关闭当前线程并释放我们创建的资源(在本例中主要是 Mutex)。
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
构造函数中:
(这用于在新实例为 运行 时应用程序的 Main Window 被隐藏的情况,因此 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();
}));
}
});
}
}
如果您还在寻找答案。有一个很好的例子 here 使用 windows 消息来恢复之前的实例。即使与 FindWindow 相反,第一个实例被最小化它也能工作在这种情况下不起作用。