在做客户端的过程中,我们通常会希望程序实例只运行一次,也就是只希望同时启动一个程序。所以今天就总结一下几种防止程序重复启动的方式:
QLockFile 使用文件提供在不同的进程间的锁。锁文件可以放置多个进程同时访问同一资源。例如磁盘上的配置文件、套接字、端口、共享内存区域等。使用的时候trylock()对资源进行上锁。当程序运行的时候,上锁成功后,会自动生成文件(例如生成的是磁盘上的文件),文件中包含进程ID, 进程名称,当前用户名这三个信息。
正常退出时,lock文件会被自动删除。如果程序崩溃,lock文件还会继续存在,当然也会继续阻止进程启动。
由于这个原因,QFileLock尝试去lock的时候,会首先根据写入文件的进程ID,去查找进程里所有的进程ID。如果没有文件中的进程ID,则认为lock文件已过期,并重新lock成功。
如果恰巧所有的进程里,刚好有这个进程ID,这时候就会把记录lock文件里的进程名称进行比较。
如果进程名也相同,则上锁失败,启动进程失败。如果只是进程ID相同,但进程名不同,还是会认为lock文件已经过期。可以加锁。
此外,QLockFile还考虑了锁文件最后修改时间(默认30秒)。如果发现锁文件已经过期,则删除。
具体使用时:
QLockFile *lockFile = new QLockFile("temp/appName.app.lock");
if (!lockFile ->tryLock(2000)) { //上锁失败,不能启动
error = AE_ACQUIRE_LOCK;
return error;
}
QT程序中,提供了QSharedMemory来让单一的线程或者进程锁定共享内存。来保证线程或者进程互斥。
使用的时候很简单
QSharedMemory singleton(a.applicationName());
if(!singleton.create(1)) { //已经存在的
return -1;
}
我们在处理线程同步的时候,会通常用到互斥量。它是一个内核对象。系统中一次只能创建一个。 如果你再次创建一个同名的就会出错。就是理由这个原理来达到只能同时运行一次的效果。
#include
#include
HANDLE hMutex = CreateMutex(NULL, FALSE, _T("TestAppName"));
if(GetLastError() == ERROR_ALREADY_EXISTS){ //如果已经存在同名的Mutex会得到这个错误.
CloseHandle(hMutex);
return FALSE;
}
我们可以根据进程名称,通过枚举当前所有进程,检查是否已经在运行。由于当前程序运行的时候,进程中肯定已经存在了一个我们要检测的进程名称,所以,最终检测所有的进程名称至少会有一个。所以程序已经运行的时候,至少会检测到两个进程名。
我们就可以通过检测所有的进程名有2次及其以上出现 就可以说明程序已经启动了。
static bool MainWindow::running()
{
int count = 0;
PROCESSENTRY32 pe32;
pe32.dwSize=sizeof(pe32);
HANDLE hProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
BOOL bMore=::Process32First(hProcessSnap,&pe32);
while(bMore) {
int len= WideCharToMultiByte(CP_ACP, 0, pe32.szExeFile, wcslen(pe32.szExeFile),
NULL, 0, NULL, NULL);
char* m_char = new char[len+1];
WideCharToMultiByte(CP_ACP, 0, pe32.szExeFile, wcslen(pe32.szExeFile),
m_char, len, NULL, NULL);
m_char[len]='\0';
if(strcmp("singleApp.exe", m_char) == 0 ) { //进程名称
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe32.th32ProcessID);
if(hProcess != NULL) {
count++; //检测到进程名称出现过一次
}
}
bMore=::Process32Next(hProcessSnap,&pe32);
delete[] m_char;
}
return count > 1 ? true : false;;
}
//main.cpp
bool runing = MainWindow::running();
if (runing) { //程序已经启动
return -1;
}
我们可以通过windows系统函数,通过程序的窗口名称(objname),来查找窗体是否存在,如果存在标识程序已经启动。我们就不允许本次启动。
如果这样我们不建议将主窗口名称设置为常见的名称。最好设置有标识性质的,防止与其他程序窗口名称相同。
我们可以使用spy++ 查看窗体的名称。
使用的时候:
#include
#include
//main.cpp
HWND hPBWnd = ::FindWindoww(stringToLPCWSTR("SelfAppMainWindow"), NULL); //通过窗口名称查找窗体是否存在
if (NULL != hPBWnd) { //窗口存在,不允许启动
return -1;
}
//continue...