VC保證應用程序只有一個實例在運行


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
{
 // 直接啟動
}


【注意,代碼應該放在程序的入口處】


其實上面的方法可以兩種進行組合來實現一些比較特殊的需求,具體怎樣就自己去想了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM