zoukankan      html  css  js  c++  java
  • VC保证应用程序只有一个实例在运行

    HANDLE m_hMutex = ::CreateMutex(NULL,TRUE,m_pszName);
    if (GetLastError() == ERROR_ALREADY_EXISTS)
    {
        AfxMessageBox("您已经运行了本软件!");//弹出对话框确认不能运行第二个实例。
         return FALSE;
    }


    让程序只运行一个实例的四种方法


    源代码下载:http://d.download.csdn.net/down/907655/magictong


     


    综述:让一个程序只运行一个实例的方法有多种,但是原理都类似,也就是在程序创建前,有窗口的程序在窗口创建前,检查系统中是否已经设置了某些特定标志了,如果有说明已经有一个实例在运行了,则当前程序通知用户怎样怎样,然后程序退出,当然方法有这么多,各自也就有自己的优缺点了。<注意下面的程序都是分块拷贝的>


     


    方法一:
    我用得做多的方法是创建互斥体Mutex,使用Mutex代码比较简洁,但是此时不能取得已经启动的实例窗口局柄,因此无法激活已经启动的实例窗口,代码如下:
    // -------------------------------------------------------------------------
    // 函数  : CreateSendingWNDList
    // 功能  : 创建互斥量,用于保证只启动一个进程
    // 返回值 : int 
    //            成功   0
    //     失败   -1
    //     存在进程实例 1
    // 附注  : 
    // -------------------------------------------------------------------------
    int CreateSendingWNDList(const TCHAR *pstrKSCoreAppName)
    {
     //-------防止多次起动----------  
     HANDLE hMutex = ::CreateMutex(0, true, pstrKSCoreAppName);
     int nRet = 0;
     if (hMutex)
     { 
      if(GetLastError() == ERROR_ALREADY_EXISTS) 
      {
       nRet = 1;
      }
      else
      {
       nRet = 0;
      }
     }
     else
     {
      nRet = -1;
     }


     return nRet;
    }


    // 在创建窗口前调用下面代码
    switch(CreateSendingWNDList(g_strKSCoreAppName))
    {
    case 0: 
     // 正常启动
     // TODO……


     break;  
    case 1:  
     // 已存在进程,退出
     {
      ::MessageBox(NULL, TEXT("已经有一个实例在运行了。"), TEXT("注意"), MB_OK);
     }


    case -1:// 无法创建,退出
    default: 
     return FALSE;
    }


     


    方法二:
    一般来说,使程序只运行一个实例的最简单的方法当然是使用FindWindow()查找主窗口,如果主窗口已经存在了,当然说明已经有一个实例运行了。代码如下:
    // 这种方法有缺陷,窗口名字改变之后就再也找不到了,FindWindow()的参数ClassName和Caption比较难取得。
    HWND hWnd = FindWindow(NULL, TEXT("SingleInstanceFW"));
    if(IsWindow(hWnd))
    {
     ::MessageBox(NULL, TEXT("已经有一个实例在运行了。"), TEXT("注意"), MB_OK);
     ::ShowWindow(hWnd, SW_NORMAL);     // 显示
     ::SetForegroundWindow(hWnd);       // 激活
     return FALSE;
    }


     


    方法三:


    这种方法相比上面两种方法,避免上面两种方法的缺点,通过SetProp()为程序主窗口设置一个特殊的Property,然后在启动时遍历所有的窗口,找出包含着个Property的窗口局柄


    。【这个附加的窗口属性在窗口销毁时也应该销毁】这个方法的缺点就是代码比较多一点,如下:


    // 声明全局的 属性 名和 属性值
    TCHAR g_strKSCoreAppName[] = _T("AFX_KSInstall_CPP__12036F8B_8301_46e2_ADC5_A14A44A85877__");
    HANDLE g_hValue = (HANDLE)1022;


    // 定义枚举窗口回调函数
    BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam)
    {
     //TCHAR str[200] = {0};
     //::GetWindowText(hwnd, str, 200);
     HANDLE h = GetProp(hwnd, g_strKSCoreAppName);
     if(h == g_hValue)
     {
      *(HWND*)lParam = hwnd;
      return FALSE;
     }
     return TRUE;
    }


    // 主窗口创建前判断
    HWND oldHWnd = NULL;
    ::EnumWindows(EnumWndProc,(LPARAM)&oldHWnd);    //枚举所有运行的窗口
    if (oldHWnd != NULL)
    {
     ::MessageBox(NULL, TEXT("已经有一个实例在运行了。"), TEXT("注意"), MB_OK);


     ::ShowWindow(oldHWnd, SW_NORMAL);     // 显示
     ::SetForegroundWindow(oldHWnd);       // 激活
     return FALSE;
    }


    // 主窗口创建后设置,为窗口附加一个属性
    ::SetProp(m_hWnd, g_strKSCoreAppName, g_hValue);


    // 主窗口退出时移除该附加属性
    ::RemoveProp(m_hWnd, g_strKSCoreAppName);


     


    方法四:(lxshanye亲测,推荐使用)


    上面的方法二和方法三都有一个弊病,不知道大家发现没,那就是依赖于窗口的存在,没有窗口的程序怎么办了,用方法一是可以的,不过方法一不太适合即时修改状态,譬如我想提供选项给用户,可以即时修改是否允许多实例,像KMP就提供了即时修改是否允许多实例,使用全局变量是一个比较好的解决方案,使用全局共享变量的方法则主要是在VC框架程序中通过编译器来实现的。通过#pragma data_seg预编译指令创建一个新节,在此节中可用volatile关键字定义一个变量,而且必须对其进行初始化。Volatile关键字指定了变量可以为外部进程访问。最后,为了使该变量能够在进程互斥过程中发挥作用,还要将其设置为共享变量,同时允许具有读、写访问权限。这可以通过#pragma comment预编译指令来通知编译器。下面给出使用了全局变量的进程互斥代码清单:


    #pragma data_seg("Shared") 
    int volatile g_lAppInstance = 0; 
    #pragma data_seg() 
    #pragma comment(linker,"/section:Shared,RWS")


    if (0 == g_lAppInstance)
    {
     g_lAppInstance = 1;
    }
    else if (1 == g_lAppInstance)
    {
     ::MessageBox(NULL, TEXT("已经有一个实例在运行了。"), TEXT("注意"), MB_OK);
     return FALSE;
    }
    else
    {
     // 直接启动
    }


    【注意,代码应该放在程序的入口处】


    其实上面的方法可以两种进行组合来实现一些比较特殊的需求,具体怎样就自己去想了。

  • 相关阅读:
    GDB 进行调试 使用心得
    libnet发包例子(tcp udp arp广播)
    epoll源码实现分析[整理]
    Linux中的时间和时间管理
    PHP生成压缩文件开发实例
    sql语句删除数据表重复字段的方法
    亿级Web系统搭建——单机到分布式集群
    微信开发之附近商家地理位置计算和腾讯地图坐标转百度地图坐标的方法
    PHP生成订单号(产品号+年的后2位+月+日+订单号)
    (用微信扫的静态链接二维码)微信native支付模式官方提供的demo文件中的几个bug修正
  • 原文地址:https://www.cnblogs.com/lxshanye/p/3093182.html
Copyright © 2011-2022 走看看