國內最大的酷站演示中心!
概述
本文是針對《基于.net平臺應用程序唯一運行實例實現》的補充,文章給出功能實現代碼,其中singleinstance類實現只允許一個實例運行,program為測試主程序入口。在代碼中標識說明文字。完整代碼下載。
主要代碼
singleinstance.cs文件,
using system;
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()
{
}
#region api實現
/// <summary>
/// 獲取應用程序進程實例,如果沒有匹配進程,返回null
/// </summary>
/// <returns>返回當前process實例</returns>
public static process getrunninginstance()
{
process currentprocess = process.getcurrentprocess();//獲取當前進程
//獲取當前運行程序完全限定名
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;
}
/// <summary>
/// 獲取應用程序句柄,設置應用程序前臺運行,并返回bool值
/// </summary>
public static bool handlerunninginstance(process instance)
{
//確保窗口沒有被最小化或最大化
showwindowasync(instance.mainwindowhandle, ws_shownormal);
//設置真實例程為foreground window
return setforegroundwindow(instance.mainwindowhandle);
}
/// <summary>
/// 獲取窗口句柄,設置應用程序前臺運行,并返回bool值,重載方法
/// </summary>
/// <returns></returns>
public static bool handlerunninginstance()
{
process p = getrunninginstance();
if (p != null)
{
handlerunninginstance(p);
return true;
}
return false;
}
#endregion
#region mutex實現
/// <summary>
/// 創建應用程序進程mutex
/// </summary>
/// <returns>返回創建結果,true表示創建成功,false創建失敗。</returns>
public static bool createmutex()
{
return createmutex(assembly.getentryassembly().fullname);
}
/// <summary>
/// 創建應用程序進程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;
}
/// <summary>
/// 釋放mutex
/// </summary>
public static void releasemutex()
{
if (mutex != null)
{
mutex.close();
}
}
#endregion
#region 設置標志實現
/// <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>
/// 釋放初始化程序運行標志,如果釋放失敗將拋出異常
/// </summary>
public static void disposerunflag()
{
if (file.exists(runflag))
{
file.delete(runflag);
}
}
/// <summary>
/// 獲取或設置程序運行標志,必須符合windows文件命名規范
/// 這里實現生成臨時文件為依據,如果修改成設置注冊表,那就不需要符合文件命名規范。
/// </summary>
public static string runflag
{ get
{
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;
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.遠程計算機程序訪問啟動應用程序(一個程序在遠程另一個在本地)。 運行cmd命令行,
第一種調用方式:
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 |
備注:o - 表示成功,x – 表示失敗,# - 程序第二個運行沒有反應 針對遠程訪問的測試,需要在系統管理工具的.net framework 2.0 configuration中進行設置授權該局域網路徑允許訪問,否則會拋出system.security.securityexception異常。
根據測試結果可見三種實現方式適用范圍不同,理想的實現是結合他們的優點進行多點判斷。
更多資源
關于.net平臺應用的開發,更多的技術文章可以訪問http://blog.csdn.net/zhzuo,對于本文的建議或意見可在網站上留言。