【问题标题】:How to Run Only One Instance of Application如何只运行一个应用程序实例
【发布时间】:2011-05-10 15:21:19
【问题描述】:

我有一个应用程序,它使用套接字连接从另一个应用程序发送和接收数据。在创建套接字时,它使用端口 4998 。

这就是我的问题所在。一旦我启动我的应用程序,套接字就会开始使用端口 4998。因此,如果我想再次执行应用程序,则会收到套接字绑定错误。

所以我想将我的应用程序实例限制为一个。这意味着如果应用程序已经在运行,并且有人尝试通过单击 exe 或快捷方式图标再次运行该应用程序,则它不应运行该程序,而是应将现有应用程序置于顶部。

【问题讨论】:

    标签: c++ windows sockets mfc


    【解决方案1】:

    在开始时创建命名事件并检查结果。如果事件已存在则关闭应用程序。

    BOOL CheckOneInstance()
    {
        m_hStartEvent = CreateEventW( NULL, TRUE, FALSE, L"EVENT_NAME_HERE" );
        if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
            CloseHandle( m_hStartEvent ); 
            m_hStartEvent = NULL;
            // already exist
            // send message from here to existing copy of the application
            return FALSE;
        }
        // the only instance, start in a usual way
        return TRUE;
    }
    

    在应用退出时关闭m_hStartEvent

    【讨论】:

    • 如果 CreatEvent() 失败,你无法判断应用是否已经在运行,所以你应该退出,而不是继续。
    【解决方案2】:

    您可以使用命名互斥锁。

    来自article的代码示例:

    WINAPI WinMain(
      HINSTANCE, HINSTANCE, LPSTR, int)
    {
      try {
        // Try to open the mutex.
        HANDLE hMutex = OpenMutex(
          MUTEX_ALL_ACCESS, 0, "MyApp1.0");
    
        if (!hMutex)
          // Mutex doesn’t exist. This is
          // the first instance so create
          // the mutex.
          hMutex = 
            CreateMutex(0, 0, "MyApp1.0");
        else
          // The mutex exists so this is the
          // the second instance so return.
          return 0;
    
        Application->Initialize();
        Application->CreateForm(
          __classid(TForm1), &Form1);
        Application->Run();
    
        // The app is closing so release
        // the mutex.
        ReleaseMutex(hMutex);
      }
      catch (Exception &exception) {
        Application->
          ShowException(&exception);
      }
      return 0;
    }
    

    【讨论】:

    • 通过首先调用 OpenMutex(),你有一个竞争条件。首先调用 CreateMutex/Ex()。它会告诉你互斥锁是否已经存在。仅当 CreateMutex() 因 ERROR_ACCESS_DENIED 错误而失败时才调用 OpenMutex()。
    • 关于如何在 Win7+ 上进行这项工作的任何提示? Global\x 互斥体在不同的用户登录会话中看不到彼此...
    • @RomanPlášil 他们确实这样做了,你一定做错了什么
    • if (!CreateMutex(NULL, TRUE, L"some GUID") || GetLastError() == ERROR_ALREADY_EXISTS) return 1; 还不够吗!?
    【解决方案3】:

    难道您还没有办法检查您的应用程序是否正在运行?谁需要 Mutex,如果端口已经被占用,你就知道应用正在运行!

    【讨论】:

    • 是的,现在我不需要显示错误,而是需要使用进程 ID 带来我的应用程序。有什么帮助吗?
    • 端口正在被使用,并不意味着你的应用程序正在使用它。
    • @OJ,好收获。该端口可能被其他应用程序使用
    • 您的应用程序只运行一次,因此警告用户(更好的是,告诉用户哪个应用程序正在使用该端口),让他们选择关闭该应用程序,以便您的应用程序可以尝试重新绑定端口。
    • “如果其他应用程序正在使用该端口,你打算做什么不同的事情?” - 显示不同的错误提示将是一个相当明显的选择。 “[I]如果端口已经被占用,你知道应用程序正在运行!” - 呃,不。您知道该端口不可用。这就是你所知道的。当误导性的错误消息弹出时,贸然下结论只会让用户感到困惑。恐怕,放弃正确的解决方案并提出一个巧合的代码方案需要投票。对不起。
    【解决方案4】:

    /* 我发现需要进行必要的编辑。添加了一些额外的代码和需要的编辑。现在的那个对我来说很完美。谢谢你,Kirill V. Lyadvinsky 和 ​​Remy Lebeau 的帮助!!

    */

    bool CheckOneInstance()
    {
    
        HANDLE  m_hStartEvent = CreateEventW( NULL, FALSE, FALSE, L"Global\\CSAPP" );
    
        if(m_hStartEvent == NULL)
        {
        CloseHandle( m_hStartEvent ); 
            return false;
        }
    
    
        if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
    
            CloseHandle( m_hStartEvent ); 
            m_hStartEvent = NULL;
            // already exist
            // send message from here to existing copy of the application
            return false;
        }
        // the only instance, start in a usual way
        return true;
    }
    

    /* 上面的代码即使在尝试从不同的登录名打开第二个实例时也可以工作,而第一个登录名是打开的,其实例正在运行。 */

    【讨论】:

    • 如果 m_hStartEvent 已经为 NULL,可能最好跳过 CloseHandle( m_hStartEvent );
    【解决方案5】:

    当您的应用程序初始化时,创建一个互斥体。如果它已经存在,则找到现有的应用程序并将其置于前台。如果应用程序的主窗口有固定的标题,使用FindWindow 很容易找到。

    m_singleInstanceMutex = CreateMutex(NULL, TRUE, L"Some unique string for your app");
    if (m_singleInstanceMutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) {
        HWND existingApp = FindWindow(0, L"Your app's window title");
        if (existingApp) SetForegroundWindow(existingApp);
        return FALSE; // Exit the app. For MFC, return false from InitInstance.
    }
    

    【讨论】:

    • 如果窗口类名是唯一的,为什么不只使用 FindWindow()?
    • @LovelyHanibal 两个实例都可以在任何一个创建其窗口之前通过它们的 FindWindow 检查。
    【解决方案6】:

    您可以在 WindowMain 函数中通过调用 FindWindow 函数来实现这一点,并在开头使用类名和主窗口的标题。如果窗口存在,您可以向用户显示一条消息,或者只是显示该窗口然后返回:

    HWND hWnd = FindWindow(lzClassName, lzWindowText);
    if(hWnd!=NULL)
    {
        ShowWindow(hWnd, SW_SHOW);
        return 0;
    }
    
    WNDCLASSEX wcex;
    /* register window class */
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_OWNDC;
    wcex.lpfnWndProc = WindowProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    
    ...
    

    【讨论】:

      猜你喜欢
      • 2023-03-05
      • 2011-07-20
      • 1970-01-01
      • 2012-05-17
      • 1970-01-01
      • 1970-01-01
      • 2012-08-31
      • 2010-10-29
      相关资源
      最近更新 更多