本节内容
- 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;
}