有时为了确保业务数据的完整性,会要求程序只能运行一个实例,那么如何做到这一点了?本文就是介绍如何确保程序只运行一个实例,再次打开程序,将激活原先运行的那个实例。要做到这一点其实并不复杂,只要在程序启动时,尝试打开一个互斥体的名称,如果启动的是第一个实例,那么将会触发WaitHandleCannotBeOpenedException异常,那么我们就在这个异常处理程序中启动程序。否则它将正常执行到下一个语句,那么我们就在打开互斥体的名称后面的语句中去激活已经运行的程序。
下面请看代码:
[STAThread]
static void Main()
... {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Mutex mutex = null;
try
...{
mutex = Mutex.OpenExisting(Common.APP_NAME);
Process[] p = Process.GetProcessesByName(Common.APP_NAME);
if (p.Length > 0)
...{
IntPtr mainWindowHandle = p[0].MainWindowHandle;
if (mainWindowHandle == IntPtr.Zero) //缩小至托盘时MainWindowHandle为零
...{
mainWindowHandle = Win32Api.FindWindow(null, "数据交换系统");
bool ok = Win32Api.SendMessage(mainWindowHandle, Common.UM_SHOWWINDOW, IntPtr.Zero, IntPtr.Zero);
}
else
Win32Api.ShowWindow(mainWindowHandle, WindowShowStyle.ShowNormal);
bool bInvert = true;
Win32Api.FlashWindow(mainWindowHandle, bInvert);
Win32Api.BringWindowToTop(mainWindowHandle);
Win32Api.SetActiveWindow(mainWindowHandle);
}
}
catch (WaitHandleCannotBeOpenedException e)
...{
mutex = new Mutex(true, Common.APP_NAME);
GC.KeepAlive(mutex); //防止过早回收
Application.Run(new frmMain());
mutex.ReleaseMutex();
}
catch (UnauthorizedAccessException e)
...{
MessageBox.Show("无权访问程序");
}
catch (System.Exception e)
...{
}
}
Win32Api类:
public static class Win32Api
... {
[DllImport("user32", SetLastError = true)]
public static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);
[DllImport("user32", SetLastError = true)]
public static extern bool FlashWindow(IntPtr hWnd, bool bInvert);
[DllImport("user32.dll", SetLastError = true)]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SendMessage(IntPtr hWnd, int nMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
WindowShowStyle枚举:
public enum WindowShowStyle : uint
... {
Hide = 0,
ShowNormal = 1,
ShowMinimized = 2,
ShowMaximized = 3,
Maximize = 3,
ShowNormalNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActivate = 7,
ShowNoActivate = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimized = 11
}
static void Main()
... {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Mutex mutex = null;
try
...{
mutex = Mutex.OpenExisting(Common.APP_NAME);
Process[] p = Process.GetProcessesByName(Common.APP_NAME);
if (p.Length > 0)
...{
IntPtr mainWindowHandle = p[0].MainWindowHandle;
if (mainWindowHandle == IntPtr.Zero) //缩小至托盘时MainWindowHandle为零
...{
mainWindowHandle = Win32Api.FindWindow(null, "数据交换系统");
bool ok = Win32Api.SendMessage(mainWindowHandle, Common.UM_SHOWWINDOW, IntPtr.Zero, IntPtr.Zero);
}
else
Win32Api.ShowWindow(mainWindowHandle, WindowShowStyle.ShowNormal);
bool bInvert = true;
Win32Api.FlashWindow(mainWindowHandle, bInvert);
Win32Api.BringWindowToTop(mainWindowHandle);
Win32Api.SetActiveWindow(mainWindowHandle);
}
}
catch (WaitHandleCannotBeOpenedException e)
...{
mutex = new Mutex(true, Common.APP_NAME);
GC.KeepAlive(mutex); //防止过早回收
Application.Run(new frmMain());
mutex.ReleaseMutex();
}
catch (UnauthorizedAccessException e)
...{
MessageBox.Show("无权访问程序");
}
catch (System.Exception e)
...{
}
}
Win32Api类:
public static class Win32Api
... {
[DllImport("user32", SetLastError = true)]
public static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);
[DllImport("user32", SetLastError = true)]
public static extern bool FlashWindow(IntPtr hWnd, bool bInvert);
[DllImport("user32.dll", SetLastError = true)]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SendMessage(IntPtr hWnd, int nMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
WindowShowStyle枚举:
public enum WindowShowStyle : uint
... {
Hide = 0,
ShowNormal = 1,
ShowMinimized = 2,
ShowMaximized = 3,
Maximize = 3,
ShowNormalNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActivate = 7,
ShowNoActivate = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimized = 11
}
上面在最小化到图标时,MainWindowHandle句柄是0,可以通过FindWindow来找到其句柄,但是这样并不可靠,其实还可以通过在第一次启动时在内存中建立一个句柄,然后第二次运行时去取那个句柄就行了。也就是利用CreateFileMapping那一套Api来操作。上面还提到了个消息常量(UM_SHOWWINDOW),这是我自己定义的一个消息,用于显示主窗口。其值可以是WM_USER(0X400)之后的值。然后在主窗口中重载WndProc方法
protected
override
void
WndProc(
ref
System.Windows.Forms.Message m)
... {
if(m.Msg == Common.UM_SHOWWINDOW)
...{
//怎么显示窗口,自己处理吧
}
base.WndProc(ref m);
}
... {
if(m.Msg == Common.UM_SHOWWINDOW)
...{
//怎么显示窗口,自己处理吧
}
base.WndProc(ref m);
}