時間:2006-7-2
本文是針對《基於.Net平臺應用程序唯一運行實例實現》的補充,文章給出功能實現代碼,其中SingleInstance類實現只允許一個實例運行,Program爲測試主程序入口。在代碼中標識說明文字。完整代碼下載。
SingleInstance.cs文件,
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;
/*------------------------------------------------
鄭佐 2006-07-01 http://blog.csdn.net/zhzuo
--------------------------------------------------*/
namespace Zhengzuo.CSharpCode
{
///<summary>
///只啓動一個應用程序實例控制類
///</summary>
public static class SingleInstance
{
private const int WS_SHOWNORMAL = 1;
[DllImport("User32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
//標誌文件名稱
private static string runFlagFullname = null;
//聲明同步基元
private static Mutex mutex = null;
///<summary>
/// static Constructor
///</summary>
static SingleInstance()
{
}
///<summary>
///獲取應用程序進程實例,如果沒有匹配進程,返回Null
///</summary>
///<returns>返回當前Process實例</returns>
public static Process GetRunningInstance()
{
//獲取當前運行程序完全限定名
string currentFileName = currentProcess.MainModule.FileName;
//獲取進程名爲ProcessName的Process數組。
Process[] processes = Process.GetProcessesByName(currentProcess.ProcessName);
//遍歷有相同進程名稱正在運行的進程
foreach (Process process in processes)
{
if (process.MainModule.FileName == currentFileName)
{
if (process.Id != currentProcess.Id)//根據進程ID排除當前進程
return process;//返回已運行的進程實例
}
}
return null;
}
///獲取應用程序句柄,設置應用程序前臺運行,並返回bool值
///</summary>
public static bool HandleRunningInstance(Process instance)
{
//確保窗口沒有被最小化或最大化
ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL);
//設置真實例程爲foreground window
return SetForegroundWindow(instance.MainWindowHandle);
}
///獲取窗口句柄,設置應用程序前臺運行,並返回bool值,重載方法
///</summary>
///<returns></returns>
public static bool HandleRunningInstance()
{
Process p = GetRunningInstance();
if (p != null)
{
HandleRunningInstance(p);
return true;
}
return false;
}
///<summary>
///創建應用程序進程Mutex
///</summary>
///<returns>返回創建結果,true表示創建成功,false創建失敗。</returns>
public static bool CreateMutex()
{
return CreateMutex(Assembly.GetEntryAssembly().FullName);
}
///創建應用程序進程Mutex
///</summary>
///<param name="name">Mutex名稱</param>
///<returns>返回創建結果,true表示創建成功,false創建失敗。</returns>
public static bool CreateMutex(string name)
{
bool result = false;
mutex = new Mutex(true, name, out result);
return result;
}
///釋放Mutex
///</summary>
public static void ReleaseMutex()
{
if (mutex != null)
{
mutex.Close();
}
}
///<summary>
///初始化程序運行標誌,如果設置成功,返回true,已經設置返回false,設置失敗將拋出異常
///</summary>
///<returns>返回設置結果</returns>
public static bool InitRunFlag()
{
if (File.Exists(RunFlag))
{
return false;
}
using (FileStream fs = new FileStream(RunFlag, FileMode.Create))
{
}
return true;
}
///釋放初始化程序運行標誌,如果釋放失敗將拋出異常
///</summary>
public static void DisposeRunFlag()
{
if (File.Exists(RunFlag))
{
File.Delete(RunFlag);
}
}
///獲取或設置程序運行標誌,必須符合Windows文件命名規範
///這裏實現生成臨時文件爲依據,如果修改成設置註冊表,那就不需要符合文件命名規範。
///</summary>
public static string RunFlag
{
{
if(runFlagFullname == null)
{
string assemblyFullName = Assembly.GetEntryAssembly().FullName;
//CommonApplicationData://"C://Documents and Settings//All Users//Application Data"
string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
//"C://Program Files//Common Files"
//string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles);
runFlagFullname = Path.Combine(path, assemblyFullName);
}
return runFlagFullname;
}
set
{
runFlagFullname = value;
}
}
#endregion
}
Program.cs文件,
using System.Windows.Forms;
using System.Diagnostics;
using Zhengzuo.CSharpCode;
/*------------------------------------------------
鄭佐 2006-07-01 http://blog.csdn.net/zhzuo
--------------------------------------------------*/
namespace Zhengzuo.Test.WinGui
{
static class Program
{
[STAThread]
static void Main(string[] args)
{
if (args.Length == 0) //沒有傳送參數
{
Process p = SingleInstance.GetRunningInstance();
if (p != null) //已經有應用程序副本執行
{
SingleInstance.HandleRunningInstance(p);
}
else //啓動第一個應用程序
{
RunApplication();
}
}
else //有多個參數
{
switch (args[0].ToLower())
{
case "-api":
if (SingleInstance.HandleRunningInstance() == false)
{
RunApplication();
}
break;
case "-mutex":
if (args.Length >= 2) //參數中傳入互斥體名稱
{
if ( SingleInstance.CreateMutex(args[1]) )
{
RunApplication();
SingleInstance.ReleaseMutex();
}
else
{
//調用SingleInstance.HandleRunningInstance()方法顯示到前臺。
MessageBox.Show("程序已經運行!");
}
}
else
{
if (SingleInstance.CreateMutex())
{
RunApplication();
SingleInstance.ReleaseMutex();
}
else
{
//調用SingleInstance.HandleRunningInstance()方法顯示到前臺。
MessageBox.Show("程序已經運行!");
}
}
break;
case "-flag"://使用該方式需要在程序退出時調用
if (args.Length >= 2) //參數中傳入運行標誌文件名稱
{
SingleInstance.RunFlag = args[1];
}
try
{
if (SingleInstance.InitRunFlag())
{
RunApplication();
SingleInstance.DisposeRunFlag();
}
else
{
//調用SingleInstance.HandleRunningInstance()方法顯示到前臺。
MessageBox.Show("程序已經運行!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
break;
default:
MessageBox.Show("應用程序參數設置失敗。");
break;
}
}
}
static void RunApplication()
{
Application.EnableVisualStyles();
Application.Run(new MainForm());
}
}
}
功能測試類別包括下面五類,
1.本地系統同一應用程序目錄;
2.本地系統同一應用程序修改運行文件名稱使兩次運行名稱不同;
3.本地系統兩次運行程序目錄不同,不修改文件名稱;
4.本地系統不同會話用戶登錄啓動應用程序;
5.遠程計算機程序訪問啓動應用程序(一個程序在遠程另一個在本地)。
第一種調用方式:
WindowsApplication1.exe
或 WindowsApplication1.exe –api
第二種調用方式:
WindowsApplication1.exe –mutex
或WindowsApplication1.exe –mutex {F140AE26-626C-42f8-BD49-45025742205E}
第三種調用方式:
WindowsApplication1.exe –flag
或WindowsApplication1.exe –flag c:/blog.csdn.net.zhzuo
匹配/互斥/標誌 | 1同一目錄 | 2修改名稱 | 3不同目錄 | 4不同用戶 | 5遠程訪問 |
1同一目錄 | O/O/O | ||||
2修改名稱 | X/O/O | ||||
3不同目錄 | X/O/O | ||||
4不同用戶 | #/X/O | ||||
5遠程訪問 | X/O/O |
根據測試結果可見三種實現方式適用範圍不同,理想的實現是結合他們的優點進行多點判斷。