先看原文,后面简单翻译一下:Creating a Single Instance Application in C#
Sometimes, it's desirable to ensure that there is only ever one instance of your application running at any given time. Take Windows Live Messenger for instance - if you try to launch it whilst it is already running, it will just bring itself to the foreground instead.
Unfortunately, a lot of people try to recreate this behavior by simply checking if a process with the same name is currently running. As K. Scott Allen explains, this is not a good idea. The correct way to implement a single instance application, is to use a named mutex.
The word mutex is short for mutual exclusion, and is a synchronisation object that can only be owned by a single thread at any given time. Specifying a name for the mutex is optional - an unnamed mutex is scoped to the current process, while a named one is associated with an operating system object and can thus be used for interprocess synchronisation. Quite simply then, we can launch our application like this:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
[STAThread]
static
void
Main()
{
bool
createdNew
=
true
;
using
(Mutexmutex
=
new
Mutex(
true
,
"
MyApplicationName
"
,
out
createdNew))
{
if
(createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(
false
);
Application.Run(
new
FrmMain());
}
}
}
That ensures that there's only a single instance of our application running. Now, the above code just 'does nothing' if the application is already running - it would be nice if it instead tried to give the main window focus. To do this, we need to find the process instance, and then pinvoke the SetForeGroundWindow method of the Win32 API. Our final Main method then looks like this:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
[DllImport(
"
user32.dll
"
)]
[
return
:MarshalAs(UnmanagedType.Bool)]
private
static
extern
bool
SetForegroundWindow(IntPtrhWnd);
///
<summary>
///
Themainentrypointfortheapplication.
///
</summary>
[STAThread]
static
void
Main()
{
bool
createdNew
=
true
;
using
(Mutexmutex
=
new
Mutex(
true
,
"
MyApplicationName
"
,
out
createdNew))
{
if
(createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(
false
);
Application.Run(
new
FrmMain());
}
else
{
Processcurrent
=
Process.GetCurrentProcess();
foreach
(Processprocess
in
Process.GetProcessesByName(current.ProcessName))
{
if
(process.Id
!=
current.Id)
{
SetForegroundWindow(process.MainWindowHandle);
break
;
}
}
}
}
}
这篇文章说,很多人通过简单地检查正在运行的进程名(通过for循环,请自行搜索其他文章,已有很多例子)来判断自己的程序是否在运行,这不是一个好办法。正确的做法是使用Mutex类,Mutex就是互斥的意思。
作者首先给出了互斥的代码(没有必要使用Mutex.WaitOne()方法) ,作者使用了using来释放资源。最后作者引入了Win32 API来让窗体重新获得焦点。
这个例子没有完全达到我们所预期的功能,当运行一个实例并最小化,再次运行这个实例时,窗口仅获得焦点,并不会恢复,在SetForegroundWindow(process.MainWindowHandle);后加入一行代码就可以搞定了,需要使用Win32 API,最终代码如下:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
using
System;
using
System.Runtime.InteropServices;
using
System.Threading;
using
System.Windows.Forms;
using
System.Diagnostics;
namespace
WinApp
{
static
class
Program
{
[DllImport(
"
user32.dll
"
)]
[
return
:MarshalAs(UnmanagedType.Bool)]
private
static
extern
bool
SetForegroundWindow(IntPtrhWnd);
[DllImport(
"
user32.dll
"
)]
private
static
extern
bool
ShowWindowAsync(IntPtrhWnd,
int
nCmdShow);
private
const
int
SW_HIDE
=
0
;
//
隐藏窗口,活动状态给令一个窗口
private
const
int
SW_SHOWNORMAL
=
1
;
//
用原来的大小和位置显示一个窗口,同时令其进入活动状态
private
const
int
SW_SHOWMINIMIZED
=
2
;
//
最小化窗口,并将其激活
private
const
int
SW_SHOWMAXIMIZED
=
3
;
//
最大化窗口,并将其激活
private
const
int
SW_SHOWNOACTIVATE
=
4
;
//
用最近的大小和位置显示一个窗口,同时不改变活动窗口
private
const
int
SW_RESTORE
=
9
;
//
用原来的大小和位置显示一个窗口,同时令其进入活动状态
private
const
int
SW_SHOWDEFAULT
=
10
;
//
根据默认创建窗口时的样式来显示
///
<summary>
///
应用程序的主入口点。
///
</summary>
[STAThread]
static
void
Main()
{
bool
createdNew
=
true
;
using
(Mutexmutex
=
new
Mutex(
true
,
"
MyApplicationName
"
,
out
createdNew))
{
if
(createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(
false
);
Application.Run(
new
FrmMain());
}
else
{
Processcurrent
=
Process.GetCurrentProcess();
foreach
(Processprocess
in
Process.GetProcessesByName(current.ProcessName))
{
if
(process.Id
!=
current.Id)
{
SetForegroundWindow(process.MainWindowHandle);
ShowWindowAsync(process.MainWindowHandle,SW_RESTORE);
break
;
}
}
}
}
}
}
}