天天看點

C++禁止程式重複運作

本節内容

  • ​​1、使用核心對象​​
  • ​​2、使用共享資料段​​
  • ​​3、使用信号量(Semaphore)​​
  • ​​4、使用事件Event​​
  • ​​後記​​

1、使用核心對象

因為核心對象是可以跨程序存在的,是以我們可以通過建立一個命名互斥體(Mutex)核心對象來判斷,當用同一個名字的來建立Mutex時,CreateMutex會傳回一個指向該互斥體的句柄,但是GetLastError會得到ERROR_ALREADY_EXISTS的返值。是以我們就可以判斷程式已有一個執行個體在運作。

m_hmutex = ::CreateMutex( NULL,FALSE,appID);
if(m_hmutex == NULL) return FALSE; 
if( ::GetLastError() == ERROR_ALREADY_EXISTS )
{
    return FALSE;
}
else
{
    return TRUE;
}      
HANDLE RunOneByMutex(const char* appString)
{
  ::SetLastError(NO_ERROR);
  HANDLE hMutex = ::CreateMutex(NULL, FALSE, appString);
  if (::GetLastError() == ERROR_ALREADY_EXISTS) {
    return NULL;  
  }

  return hMutex;
}      

2、使用共享資料段

背景知識:EXE和DLL檔案映像由許多區組成如代碼在.text段中,初始化資料在.data段中,未初始化資料在.bss段中。系統在加載EXE和DLL時,實際上是使用了記憶體映射,為了減少加載時間,同一EXE檔案多個執行個體實際在系統中隻有一份。但一般地如果其中某個執行個體對某個資料區進行寫時,系統會使用Copy-On-Write機制将這個資料區在虛拟記憶體中複制一份出來,并映射到該執行個體原先的位址空間,也就實作了程序資料的唯一性,而不會幹擾其它程序。但是我們可以通過設定讓系統關閉掉這個機制。哪麼如何做呢?

在Visual Studio中你可以程式中加上以下幾行:

#pragmacomment(linker,"/SECTION:Share,RWS") //訓示編譯器Share是可讀寫和共享的,當然你也可以通過設定連結器選項直接加上 /SECTION:Share,RWS,不過我更喜歡這個,是以其他朋友就不必自已去設定這個選項了。

// 開始自已的資料段
#pragmadata_seg("Share")
//必需初始化,否則不會将編譯器不會将其放入Share這個區
int g_AppInit =0;  
#pragmadata_seg()      

還有一種将某個變量置于特别資料段的方式

__declspec(allocate("Share")) int g_AppInit =0;      

這種方式的好處是無論你初不初始化這個變量都将置于該Share段内

哪麼如何判斷呢是否啟動了執行個體呢,很簡單,看以下代碼

g_AppInit ++ ;
if(g_AppInit >1)
{
  AfxMessageBox("A instance are runed!");
  return FALSE;
}      

相關的問題:如何通知前一個執行個體

解決了重複啟動的問題,為了獲得更佳的使用者體驗,往往我們要使前一個執行個體激活,如何做呢?使用消息是一個不錯的方法。首先你需要在啟動程式時登記一個全局消息。

WM_APPACTIVE = ::RegisterWindowMessage(appID);      

相同的appID字元串會給出相同的消息值,并且總是在0xC000- 0xFFFF區間中,然後當你發現已啟動程式執行個體時通知前一個執行個體:

DWORD dectype = BSM_APPLICATIONS;   //僅向應用程式發送
BroadcastSystemMessage(BSF_POSTMESSAGE,&dectype, WM_APPACTIVE, 0,0);      

3、使用信号量(Semaphore)

bool RunOneBySemaphore(const char* appString)
{
  ::SetLastError(NO_ERROR);
  HANDLE hSem = CreateSemaphore(NULL, 1, 1, appString);
  if(hSem)
  {
    if(ERROR_ALREADY_EXISTS == GetLastError())
    {
      CloseHandle(hSem);
      hSem = NULL;

      //AfxMessageBox(_T("已有一個執行個體在運作!"));
      return false;
    }
  }
  else
  {
    //AfxMessageBox(_T("建立唯一對象失敗,程式退出!"));
    return false;
  }
  return hSem != NULL;
}      

4、使用事件Event

#ifdef _AFX
// Makes sure only one instance of the application is running.
BOOL IsSingleInstance( const char* AppTitle /*= NULL */ )
{
  HANDLE hEvent;
  hEvent = ::CreateEvent(NULL,FALSE,FALSE, (AppTitle==NULL ? AfxGetAppName() : AppTitle ));
  if( hEvent==NULL ) {
    // Something REALLY went wrong
    return FALSE;
  };
  if( ::GetLastError()==ERROR_ALREADY_EXISTS ) {
    // Some other instance is running!
    ::CloseHandle( hEvent );
    return FALSE;
  };
  // System closes handle automatically when process terminates
  return TRUE;
}      

後記