zoukankan      html  css  js  c++  java
  • 转程序只启动一个实例的几种方法

      有些时候,我们要求一个程序在系统中只能启动一个实例。比如,Windows自带的播放软件Windows Medea Player在Windows里就只能启动一个实例。原因很简单,如果同时启动几个实例,却播放不同的文件,那么声音和图像就会引起混乱。在设计模式中,就有一个SINGLETON模式,该模式就是让类只有一个实例。(关于SINGLETON模式,可以看我那篇《重读《设计模式》之学习笔记(三)--SINGLETON模式的疑惑 》)。
        对于程序而言,我们只有在程序启动的时候去检测某个设置,如果程序没有启动,就把设置更新为程序已经启动,然后正常启动程序;如果程序已经启动,那么就终止程序的启动。在程序退出的时候把设置恢复为程序没有启动。按照上面的思路,我们很容易就能想出下面的两种方法:
        一,文件法
        在硬盘上创建一个文件,在文件里设置一个值,根据这个值来判断程序是否已经启动。
        二,注册表法
        在注册表中创建一个键,根据该键的键值来决定是否要启动程序。
        但是,上面的两种方法,都有I/O操作。我觉得这不是最好的方法。下面就介绍两种不用I/O操作的方法。思路跟上面是一样的,在进程启动的时候去检测某个设置是否继续启动进程。由于要判断同一个程序是否已经启动一个实例,也就是说会有两个进程去访问同一个设置,所以该设置应该是可以夸进程访问的,比如上面两种方法中的文件和注册表。我们在用VC进行开发时,还可以用文件映射和互斥量。下面是详细的说明:
        VC在创建工程的时候,会自动创建一个App的类。比如,你的工程名是StarLee,那么这个App类的类名就是CStarLeeApp。在进程启动和退出的时候会分别调用该类的两个方法:InitInstance()和ExitInstance()。所以,我们的代码都是添加在这两个方法里面的。
        三,文件映射法
        首先,给App类加上一个成员变量: 

    HANDLE m_hFileMapping;

        然后,在App类的InitInstance()方法的最前面加上下面的代码:

    m_hFileMapping = CreateFileMapping(NULL, NULL, PAGE_READONLY, 013"StarLee");

    // 检测是否已经创建FileMapping
    // 如果已经创建,就终止进程的启动
    if ((m_hFileMapping != NULL) && (GetLastError() == ERROR_ALREADY_EXISTS))
    {
        CloseHandle(m_hFileMapping);
        
        MessageBox(NULL, 
    "该进程已经启动""错误", MB_OK);

        
    return FALSE;
    }

        最后,在App类的ExitInstance()方法里加上下面的代码:

    if (m_hFileMapping != NULL)
        CloseHandle(m_hFileMapping);

        四,互斥量法
        首先,给App类加上一个成员变量:

    HANDLE m_hMutex;

        然后,在App类的InitInstance()方法的最前面加上下面的代码:

    m_hMutex = CreateMutex(NULL, TRUE, "StarLee"); 

    // 检测是否已经创建Mutex
    // 如果已经创建,就终止进程的启动
    if ((m_hMutex != NULL) && (GetLastError() == ERROR_ALREADY_EXISTS)) 
    {
        ReleaseMutex(m_hMutex);

        MessageBox(NULL, 
    "该进程已经启动""错误", MB_OK);
     
        
    return FALSE;
    }

        最后,在App类的ExitInstance()方法里加上下面的代码:

    if (m_hMutex != NULL)
    {
        ReleaseMutex(m_hMutex);
        CloseHandle(m_hMutex);
    }

        上面两种方法的思路和代码添加的步骤都是一样的,当然效果也一样,选择任何一种方法都能达到让进程只启动一个实例的目的。

      我那篇《程序只启动一个实例的几种方法》发表后被推荐到了CSDN首页,有不少网友看了之后提出了一些很好的建议。其中有个网友说可以用共享变量法,我上网收集了一些资料,又经过代码测试,现在补充一下这种方法:
        五,共享变量法
        首先,在App类的cpp文件开头加上下面的代码:

    #pragma data_seg("StarLee") // 自己定义的数据段
        
    char nInstanceCount = -1// 实例个数,下文会详细用char来定义实例个数的好处
    #pragma data_seg()
    #pragma comment(linker, "/section:StarLee,rws") // 共享该数据段(r - read, w - write, s - share)

        然后,在App类的InitInstance()方法的最前面加上下面的代码:

    nInstanceCount++;
    if (nInstanceCount > 0)
    {
        MessageBox(NULL, 
    "该进程已经启动""错误", MB_OK);
            
        
    return FALSE;
    }

        最后,在App类的ExitInstance()方法里加上下面的代码:

    nInstanceCount--;

        PS:这里介绍一下上面代码中用把实例个数的类型定义为char的小技巧。我们知道char的长度为1个字节,是用-128~127之间的整型来表示的。而我们进程实例的最大值是1,而且在程序的数据段里添加的东西越少越好,所以char就成了最佳之选。

        在那篇《程序只启动一个实例的几种方法》的十几个回复中,很多网友都提到了如果进程被意外终止,恢复设置的代码将不会被调用,那样程序就不能再被启动了。为此我针对每种方法都做了测试,下面对结果进行一下分析:
        测试环境:
        Windows 2000,VC++ 6.0
        测试方法:
        1,启动程序,在任务管理器终止该进程,再次启动进程。
        2,删除App类的ExitInstance()方法里面添加的代码,编译通过以后,启动程序,关闭程序,再次启动程序。
        结果:
        对于方法一和方法二(文件法和注册表法),由于有I/O操作,如果恢复设置的代码不被调用,除非手动将设置恢复,否则程序就永远不能再被启动。这也是我不推荐这两种方法的主要原因。
        对于后3种方法(文件映射法,互斥量法和共享变量法),就算进程被异常终止,恢复设置的代码不被调用,也不会影响程序的再次启动。我觉得,这是因为这3种使用的对象(文件映射,互斥量,共享变量)虽然可以夸进程访问,但是他们还是属于创建者进程的,会随着创建者的销毁而销毁,Windows会在进程结束(无论是正常还是意外)的时候释放它们。
        另外,我在MSDN对CreateMutex的介绍中发现了下面这句话:
        The system closes the handle automatically when the process terminates. The mutex object is destroyed when its last handle has been closed.
        可以说这是明确的告诉我们互斥量在程序终止的时候会被自动释放。
        所以,在我介绍的这些让进程只启动一个实例的五种方法中,互斥量法是最好的解决方法。

  • 相关阅读:
    hdu 4027 Can you answer these queries?
    hdu 4041 Eliminate Witches!
    hdu 4036 Rolling Hongshu
    pku 2828 Buy Tickets
    hdu 4016 Magic Bitwise And Operation
    pku2886 Who Gets the Most Candies?(线段树+反素数打表)
    hdu 4039 The Social Network
    hdu 4023 Game
    苹果官方指南:Cocoa框架(2)(非原创)
    cocos2d 中 CCNode and CCAction
  • 原文地址:https://www.cnblogs.com/kex1n/p/2306249.html
Copyright © 2011-2022 走看看