讓程序只運(yùn)行一個(gè)
實(shí)例的方法有數(shù)種,但原理都是相同的,就是在程序的主窗口創(chuàng)建之前,檢查系統(tǒng)中是否已經(jīng)存在某個(gè)與本程序相關(guān)的特定標(biāo)志了。如果存在,則說明已經(jīng)有一個(gè)
實(shí)例在運(yùn)行了,當(dāng)前程序不用創(chuàng)建主窗口,直接退出即可。否則,就說明本程序是第一次運(yùn)行。各種方法所不同的是,各自檢查的標(biāo)志不同,這也使得各種方法在使用時(shí)各有利弊了。
一般來說,使程序只運(yùn)行一個(gè)
實(shí)例的最簡單的方法當(dāng)然是使用FindWindow()查找主窗口,如果主窗口已經(jīng)存在了,當(dāng)然說明已經(jīng)有一個(gè)
實(shí)例運(yùn)行了。代碼如下:
// 主窗口創(chuàng)建前
HWND hWnd = FindWindow("ClassName", "Caption");
if(IsWindow(hWnd))
{
ShowWindow(hWnd, SW_NORMAL); // 顯示
SetForegroundWindow(hWnd); // 激活
return;
}這個(gè)方法的不足之處是,F(xiàn)indWindow()的參數(shù)ClassName和Caption比較難取得。比如,凡是使用DialogBoxParam()創(chuàng)建的對(duì)話框,他們的ClassName都是“#32770”,沒有
唯一性;而使用MFC創(chuàng)建的Doc/View結(jié)構(gòu)的窗口的Caption更是會(huì)隨Doc?Name的不同而有所變化。
另
一種方法就是使用
Mutex互斥體了。代碼如下:
// 聲明全局的局柄
HANDLE g_hHandle;
// 主窗口創(chuàng)建前
g_hHandle = CreateMutex(NULL, FALSE, "Mutex Name");
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
return FALSE;
}使用
Mutex代碼比較簡潔,但是此時(shí)不能取得已經(jīng)
啟動(dòng)的
實(shí)例窗口局柄,因此無法激活已經(jīng)
啟動(dòng)的
實(shí)例窗口。
第三種方法是我認(rèn)為比較完善的方法,就是通過SetProp()為程序主窗口設(shè)置一個(gè)特殊的Property,然后在
啟動(dòng)時(shí)遍歷所有的窗口,找出包含著個(gè)Property的窗口局柄。這個(gè)方法的缺點(diǎn)就是代碼比較多。如下:
// 聲明全局的property名和property value
LPCTSTR g_szPropName = "prop name";
HANDLE g_hValue = (HANDLE)1;
// 定義枚舉窗口回調(diào)函數(shù)
BOOL CALLBACK EnumWndProc(HWND?hwnd,?LPARAM?lParam)
{
???????HANDLE?h?=?GetProp(hwnd,?g_szPropName);
???????if(?h?==?g_hValue)
???????{
?????????????*(HWND*)lParam?=?hwnd;
?????????????return?false;
???????}
???????return?true;
}
//?主窗口創(chuàng)建前判斷
HWND?hWnd?=?NULL;
EnumWindows(EnumWndProc,?(LPARAM)&hWnd);
if(IsWindow(hWnd))
{
??????ShowWindow(hWnd,?SW_NORMAL);
??????SetForegroundWindow(hWnd);
??????return?FALSE;
}
//?主窗口創(chuàng)建后設(shè)置
SetProp(m_hWnd,?g_szPropName,?g_hValue);
這個(gè)方法就是需要遍歷系統(tǒng)中所有的窗口,效率可能稍低了些。