原创 如何做到像《金山词霸》一样只运行一个实例

2008-10-26 18:36 1777 8 8 分类: 软件与OS

如何做到像《金山词霸》一样只运行一个实例<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


我们在使用《金山词霸》时发现,在《金山词霸》已经运行了的情况下,再次点击《金山词霸》的图标,那么它不会再运行另外一个《金山词霸》,而是将已有的《金山词霸》给激活,始终只能运行一个《金山词霸》的实例。


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


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


第二个问题是如何找到已经运行的应用程序实例,如果我们能够找到已运行实例主窗口的指针,即可调用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条评论)

登录后参与讨论
我要评论
0
8
关闭 站长推荐上一条 /2 下一条