让应用程序只运行一个实例的实现方法

时间:2023-03-19 22:20:52 

在我们的程序当中如果要实现类似《360软件管家》的功能,就要解决两个问题,首先是要判断该程序已有一个实例在运行,其次是要将已运行的应用程序实例激活,同时退出第二个应用程序实例。

对于第一个问题,我们可以通过设置命名互斥对象或命名信标对象,在程序启动的时候检测互斥对象或信标对象,如互斥对象或信标对象已存在,则可以判断此程序已有一个实例正在运行。

第二个问题是如何找到已经运行的应用程序实例,如果我们能够找到已运行实例主窗口的指针,即可调用SetForegroundWindow来激活该实例。我们可以通过两种形式找到已运行实例的主窗口,一种形式是通过调用FindWindowEx去查找正在运行的窗口的句柄,这种方式用得比较多一些,而本文通过另一种形式去查找正在运行的窗口的句柄。通过调用SetProp给应用程序主窗口设置一个标记,用GetDesktopWindow 可以获取Windows环境下的桌面窗口的句柄,所有应用程序的主窗口都可以看成该窗口的子窗口,接着我们就可以用GetWindow函数来获得这些窗口的句柄。然后再用Win32 SDK函数GetProp查找每一个应用程序的主窗口是否包含有我们设置的标记,这样就可以找到我们要找的第一个实例主窗口。

下面演示代码是以一个单文档应用程序为例,工程名字是Mutex。


1、在应用程序类InitInstance()函数中判断是否已有一个应用程序实例正在运行。

BOOL CMutexApp::InitInstance()

{

       //创建命名信标对象。

       HANDLE hSem=CreateSemaphore(NULL,1,1,"维新");

       if(hSem)  //信标对象创建成功。

       {

              //信标对象已经存在,则程序已有一个实例在运行。

              if(ERROR_ALREADY_EXISTS==GetLastError())

              {                 

                     CloseHandle(hSem);      //关闭信号量句柄。

 

//获取桌面窗口的一个子窗口。

                     HWND hWndPrev=::GetWindow(::GetDesktopWindow(),GW_CHILD);  

 

                     while(::IsWindow(hWndPrev))

                     {

                     //判断窗口是否有我们预先设置的标记,如有,则是我们寻找的窗口,并将它激活。

                            if(::GetProp(hWndPrev,"维新"))  

                            {

                            //如果主窗口已最小化,则恢复其大小。

                                   if (::IsIconic(hWndPrev))    

                                          ::ShowWindow(hWndPrev,SW_RESTORE);

 

                                   //将应用程序的主窗口激活。

                                   ::SetForegroundWindow(hWndPrev);

                                   return FALSE;                      //退出实例。

                            }

                            //继续寻找下一个窗口。

                            hWndPrev = ::GetWindow(hWndPrev,GW_HWNDNEXT);

                     }

                   

                     AfxMessageBox("已有一个实例在运行,但找不到它的主窗口!");

              }

       }

       else

       {

              AfxMessageBox("创建信标对象失败,程序退出!");

              return FALSE;

       }

 

       AfxEnableControlContainer();

 

       // Standard initialization

       // If you are not using these features and wish to reduce the size

       //  of your final executable, you should remove from the following

       //  the specific initialization routines you do not need.

     

#ifdef _AFXDLL

       Enable3dControls();                     // Call this when using MFC in a shared DLL

#else

       Enable3dControlsStatic();      // Call this when linking to MFC statically

#endif

 

       // Change the registry key under which our settings are stored.

       // TODO: You should modify this string to be something appropriate

       // such as the name of your company or organization.

       SetRegistryKey(_T("Local AppWizard-Generated Applications"));

 

       LoadStdProfileSettings();  // Load standard INI file options (including MRU)

 

       // Register the application's document templates.  Document templates

       //  serve as the connection between documents, frame windows and views.

 

       CSingleDocTemplate* pDocTemplate;

       pDocTemplate = new CSingleDocTemplate(

              IDR_MAINFRAME,

              RUNTIME_CLASS(CMutexDoc),

              RUNTIME_CLASS(CMainFrame),       // main SDI frame window

              RUNTIME_CLASS(CMutexView));

       AddDocTemplate(pDocTemplate);

 

       // Parse command line for standard shell commands, DDE, file open

       CCommandLineInfo cmdInfo;

       ParseCommandLine(cmdInfo);

 

       // Dispatch commands specified on the command line

       if (!ProcessShellCommand(cmdInfo))

              return FALSE;

 

       // The one and only window has been initialized, so show and update it.

       m_pMainWnd->ShowWindow(SW_SHOW);

       m_pMainWnd->UpdateWindow();

 

       return TRUE;

}

2、在框架类的OnCreate()函数中设置查找标记。

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

       if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

              return -1;

     

       if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP

              | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||

              !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

       {

              TRACE0("Failed to create toolbar/n");

              return -1;      // fail to create

       }

 

       if (!m_wndStatusBar.Create(this) ||

              !m_wndStatusBar.SetIndicators(indicators,

                sizeof(indicators)/sizeof(UINT)))

       {

              TRACE0("Failed to create status bar/n");

              return -1;      // fail to create

       }

 

       // TODO: Delete these three lines if you don't want the toolbar to

       //  be dockable

 

       m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

       EnableDocking(CBRS_ALIGN_ANY);

       DockControlBar(&m_wndToolBar);

 

     

       //设置查找标记。

       ::SetProp(m_hWnd,"维新",(HANDLE)1);

 

       return 0;

}

3、在程序退出是删除设置的标记,在框架类中响应WM_DESTROY消息,进行处理。

void CMainFrame::OnDestroy()

{

       CFrameWnd::OnDestroy();

     

       // TODO: Add your message handler code here

       //删除所设置的标记。

       ::RemoveProp(m_hWnd,"维新");

}

至此,使应用程序只运行一个实例的功能就完成了。

 

标签:只运行一个实例
0
投稿

猜你喜欢

  • 分析Java中为什么String不可变

    2023-06-05 23:33:25
  • C#实现JWT无状态验证的实战应用解析

    2022-07-28 09:20:26
  • 解析Android框架之Volley源码

    2021-09-30 19:44:28
  • IDEA实用好用插件推荐及使用方法教程详解(必看)

    2021-07-15 19:10:44
  • Android设置theme中可能遇到的坑

    2021-08-05 06:20:26
  • Android 仿网易新闻客户端分类排序功能

    2023-12-19 13:07:52
  • 一些比较实用的 Android adb 命令分享

    2023-12-22 12:39:41
  • swagger中如何给请求添加header

    2023-05-15 14:27:52
  • java显示当前运行时的参数(java运行参数)

    2023-09-07 10:03:22
  • Android双向选择控件DoubleSeekBar使用详解

    2022-02-13 10:05:29
  • 详解SpringCloud微服务架构之Hystrix断路器

    2022-03-05 05:28:56
  • Android SharedPreferences实现记住密码和自动登录界面

    2023-06-15 20:07:00
  •  Java SE 面向对象编程的3个常用接口

    2023-01-14 06:42:32
  • 使用FeignClient设置动态Url

    2022-07-04 05:55:39
  • 一文搞懂Android RecyclerView点击展开、折叠效果的实现代码

    2023-02-17 14:52:24
  • springBoot service层事务控制的操作

    2022-02-01 12:39:40
  • Android开发之自动朗读TTS用法分析

    2021-08-21 22:31:00
  • Maven学习----Maven安装与环境变量配置教程

    2021-12-04 08:20:25
  • c#中Linq to Sql 增删除的实例

    2022-08-24 02:15:34
  • java IO流 之 输出流 OutputString()的使用

    2023-08-11 23:16:30
  • asp之家 软件编程 m.aspxhome.com