天天看点

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;
}      

后记