1. 概念
防止进程多开,一般是特指防止同一个exe进程重复打开。为什么要防止进程多开呢?
- 功能设计上不让用户打开两次,如各种游戏客户端。
- 防止多进程对资源访问的冲突。如当前进程对某文件可能在进行写读操作,再打开新进程时可能的写读操作会导致出现文件访问异常甚至崩溃。
2. 方法
网上很多资料推荐利用遍历窗口来判断,如果找到对应的窗口则认为进程已经打开。这种方法其实存在很大的风险。
- 窗口创建的过程中可能因为系统消息阻塞导致创建很慢,那么在窗口创建过程中再打开进程,并通过遍历窗口来判断则可能失败。
- 窗口关闭过程中,窗口销毁了,但是此时主进程以及其他子进程可能由于某些原因(如内存申请较大,释放较慢等)导致销毁较慢。在此过程中,再次打开进程,并通过遍历窗口来判断则可能失败。
推荐用以下两种方法:
2.1. 通过互斥内核对象
所有的内核对象都是可以跨进程通信的,在此选择简单的内核对象互斥量。为了保证互斥量尽量延后释放,不建议使用手动调用ReleaseMutex来释放互斥量对象。操作系统的内核管理器会在进程销毁后自动释放所有的内核对象。
BOOL CXXXApp::InitInstance()
{
HANDLE hObject = ::CreateMutex(NULL,FALSE, _T("MutexDemo"));
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
return FALSE;
}
......
2.2. 通过共享内存段
除了内核对象外,我们还可以通过编译链接期间指定共享内存段来达到防止进程多开。此方式相比内核对象可能更可靠,因为内存段的回收是系统管理的,应该是在进程完全销毁了之后才回收清空的。
- 申请共享内存段,段名需要保证唯一性
#pragma data_seg("MutexDemo")
int g_nOnlyOne = 0;
#pragma data_seg()
#pragma comment(linker, "/Section:MutexDemo,RWS")
- 通过内存段变量进行判断
BOOL CTestFWApp::InitInstance()
{
if (1 == g_nOnlyOne)
{
return FALSE;
}
g_nOnlyOne = 1;
.......