HANDLE m_hMutex = ::CreateMutex(NULL,TRUE,m_pszName);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
AfxMessageBox("您已經運行了本軟件!");//彈出對話框確認不能運行第二個實例。
return FALSE;
}
讓程序只運行一個實例的四種方法
源代碼下載:http://d.download.csdn.net/down/907655/magictong
綜述:讓一個程序只運行一個實例的方法有多種,但是原理都類似,也就是在程序創建前,有窗口的程序在窗口創建前,檢查系統中是否已經設置了某些特定標志了,如果有說明已經有一個實例在運行了,則當前程序通知用戶怎樣怎樣,然后程序退出,當然方法有這么多,各自也就有自己的優缺點了。<注意下面的程序都是分塊拷貝的>
方法一:
我用得做多的方法是創建互斥體Mutex,使用Mutex代碼比較簡潔,但是此時不能取得已經啟動的實例窗口局柄,因此無法激活已經啟動的實例窗口,代碼如下:
// -------------------------------------------------------------------------
// 函數 : CreateSendingWNDList
// 功能 : 創建互斥量,用於保證只啟動一個進程
// 返回值 : int
// 成功 0
// 失敗 -1
// 存在進程實例 1
// 附注 :
// -------------------------------------------------------------------------
int CreateSendingWNDList(const TCHAR *pstrKSCoreAppName)
{
//-------防止多次起動----------
HANDLE hMutex = ::CreateMutex(0, true, pstrKSCoreAppName);
int nRet = 0;
if (hMutex)
{
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
nRet = 1;
}
else
{
nRet = 0;
}
}
else
{
nRet = -1;
}
return nRet;
}
// 在創建窗口前調用下面代碼
switch(CreateSendingWNDList(g_strKSCoreAppName))
{
case 0:
// 正常啟動
// TODO……
break;
case 1:
// 已存在進程,退出
{
::MessageBox(NULL, TEXT("已經有一個實例在運行了。"), TEXT("注意"), MB_OK);
}
case -1:// 無法創建,退出
default:
return FALSE;
}
方法二:
一般來說,使程序只運行一個實例的最簡單的方法當然是使用FindWindow()查找主窗口,如果主窗口已經存在了,當然說明已經有一個實例運行了。代碼如下:
// 這種方法有缺陷,窗口名字改變之后就再也找不到了,FindWindow()的參數ClassName和Caption比較難取得。
HWND hWnd = FindWindow(NULL, TEXT("SingleInstanceFW"));
if(IsWindow(hWnd))
{
::MessageBox(NULL, TEXT("已經有一個實例在運行了。"), TEXT("注意"), MB_OK);
::ShowWindow(hWnd, SW_NORMAL); // 顯示
::SetForegroundWindow(hWnd); // 激活
return FALSE;
}
方法三:
這種方法相比上面兩種方法,避免上面兩種方法的缺點,通過SetProp()為程序主窗口設置一個特殊的Property,然后在啟動時遍歷所有的窗口,找出包含着個Property的窗口局柄
。【這個附加的窗口屬性在窗口銷毀時也應該銷毀】這個方法的缺點就是代碼比較多一點,如下:
// 聲明全局的 屬性 名和 屬性值
TCHAR g_strKSCoreAppName[] = _T("AFX_KSInstall_CPP__12036F8B_8301_46e2_ADC5_A14A44A85877__");
HANDLE g_hValue = (HANDLE)1022;
// 定義枚舉窗口回調函數
BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam)
{
//TCHAR str[200] = {0};
//::GetWindowText(hwnd, str, 200);
HANDLE h = GetProp(hwnd, g_strKSCoreAppName);
if(h == g_hValue)
{
*(HWND*)lParam = hwnd;
return FALSE;
}
return TRUE;
}
// 主窗口創建前判斷
HWND oldHWnd = NULL;
::EnumWindows(EnumWndProc,(LPARAM)&oldHWnd); //枚舉所有運行的窗口
if (oldHWnd != NULL)
{
::MessageBox(NULL, TEXT("已經有一個實例在運行了。"), TEXT("注意"), MB_OK);
::ShowWindow(oldHWnd, SW_NORMAL); // 顯示
::SetForegroundWindow(oldHWnd); // 激活
return FALSE;
}
// 主窗口創建后設置,為窗口附加一個屬性
::SetProp(m_hWnd, g_strKSCoreAppName, g_hValue);
// 主窗口退出時移除該附加屬性
::RemoveProp(m_hWnd, g_strKSCoreAppName);
方法四:(lxshanye親測,推薦使用)
上面的方法二和方法三都有一個弊病,不知道大家發現沒,那就是依賴於窗口的存在,沒有窗口的程序怎么辦了,用方法一是可以的,不過方法一不太適合即時修改狀態,譬如我想提供選項給用戶,可以即時修改是否允許多實例,像KMP就提供了即時修改是否允許多實例,使用全局變量是一個比較好的解決方案,使用全局共享變量的方法則主要是在VC框架程序中通過編譯器來實現的。通過#pragma data_seg預編譯指令創建一個新節,在此節中可用volatile關鍵字定義一個變量,而且必須對其進行初始化。Volatile關鍵字指定了變量可以為外部進程訪問。最后,為了使該變量能夠在進程互斥過程中發揮作用,還要將其設置為共享變量,同時允許具有讀、寫訪問權限。這可以通過#pragma comment預編譯指令來通知編譯器。下面給出使用了全局變量的進程互斥代碼清單:
#pragma data_seg("Shared")
int volatile g_lAppInstance = 0;
#pragma data_seg()
#pragma comment(linker,"/section:Shared,RWS")
if (0 == g_lAppInstance)
{
g_lAppInstance = 1;
}
else if (1 == g_lAppInstance)
{
::MessageBox(NULL, TEXT("已經有一個實例在運行了。"), TEXT("注意"), MB_OK);
return FALSE;
}
else
{
// 直接啟動
}
【注意,代碼應該放在程序的入口處】
其實上面的方法可以兩種進行組合來實現一些比較特殊的需求,具體怎樣就自己去想了。