在項目開發中,經常要求系統在同一時刻在同一台機器上只能運行一個執行個體,可以通過這種方式實現
在InitInstance()函數中添加如下代碼(可以函數最前邊添加,也可以放後邊點,不過最好放最前邊):
Sample Code
HANDLE m_hMutex = ::CreateMutex(NULL,TRUE,m_pszName);// m_pszName為互斥體 名 如"132"或者"my"
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
AfxMessageBox("您已經運行了本軟體!");//彈出對話方塊確認不能運行第二個執行個體。
return FALSE;
}
1: 建立一基於對話方塊的工程ex1,採用預設設定
2: 用GUIDGEN.EXE產生一個全域標誌,#define one "產生的全域標誌"
本例中產生的語句如下:#define one "0xbe8e2ce1, 0xdab6, 0x11d6, 0xad, 0xd0, 0x0,
0xe0, 0x4c, 0x53, 0xf6, 0xe6"
3: 在應用程式類CEx1App::InitInstance()中,用CreateMutex函數建立一個互斥量,後調用函數GetLastError()
如果結果等於ERROR_ALREADY_EXISTS說明已經有一個執行個體在運行了這時返回FALSE.
BOOL CEx1App::InitInstance()
{
handle=::CreateMutex(NULL,FALSE,one);//handle為聲明的HANDLE類型的全域變數 ,前邊是個局部變數 故不用第4步
if(GetLastError()==ERROR_ALREADY_EXISTS)
{
AfxMessageBox("應用程式已經在運行");
return FALSE;
}
}
4:在CEx1App::ExitInstance()中,刪除這個互斥量
int CEx1App::ExitInstance()
{
CloseHandle(handle);
return CWinApp::ExitInstance();
}
------------------------------一----------------------------------------------------------------
方法一:
我用得做多的方法是建立互斥體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);
方法四:
上面的方法二和方法三都有一個弊病,不知道大家發現沒,那就是依賴於視窗的存在,沒有視窗的程式怎麼辦了,用方法一是可以的,不過方法一不太適合即時修改狀態,譬如我想提供選項給使用者,可以即時修改是否允許多執行個體,像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
{
// 直接啟動
}
【注意,代碼應該放在程式的入口處】
其實上面的方法可以兩種進行組合來實現一些比較特殊的需求,具體怎樣就自己去想了。