C++ QT PC端 防止程序重复启动的几种方式

在做客户端的过程中,我们通常会希望程序实例只运行一次,也就是只希望同时启动一个程序。所以今天就总结一下几种防止程序重复启动的方式:

1.使用QT中的 QLockFile:

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

2.共享内存 QSharedMemory

QT程序中,提供了QSharedMemory来让单一的线程或者进程锁定共享内存。来保证线程或者进程互斥。

使用的时候很简单

    QSharedMemory singleton(a.applicationName());    
    if(!singleton.create(1))  {    //已经存在的
        return -1;
    }

 

3. 使用互斥量(Mutex)

我们在处理线程同步的时候,会通常用到互斥量。它是一个内核对象。系统中一次只能创建一个。 如果你再次创建一个同名的就会出错。就是理由这个原理来达到只能同时运行一次的效果。

   #include 
   #include  


    HANDLE hMutex = CreateMutex(NULL, FALSE, _T("TestAppName"));

    if(GetLastError() == ERROR_ALREADY_EXISTS){ //如果已经存在同名的Mutex会得到这个错误.

        CloseHandle(hMutex);

        return FALSE;

    }

4. 进程名称枚举

我们可以根据进程名称,通过枚举当前所有进程,检查是否已经在运行。由于当前程序运行的时候,进程中肯定已经存在了一个我们要检测的进程名称,所以,最终检测所有的进程名称至少会有一个。所以程序已经运行的时候,至少会检测到两个进程名。

我们就可以通过检测所有的进程名有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;
}


5. 查找窗体是否存在

我们可以通过windows系统函数,通过程序的窗口名称(objname),来查找窗体是否存在,如果存在标识程序已经启动。我们就不允许本次启动。

如果这样我们不建议将主窗口名称设置为常见的名称。最好设置有标识性质的,防止与其他程序窗口名称相同。

我们可以使用spy++ 查看窗体的名称。

使用的时候:

#include 
#include 

//main.cpp

    HWND hPBWnd = ::FindWindoww(stringToLPCWSTR("SelfAppMainWindow"), NULL); //通过窗口名称查找窗体是否存在
    if (NULL != hPBWnd) { //窗口存在,不允许启动
        return -1;
    } 

    //continue...

 

 

你可能感兴趣的:(C++)