使程序只能运行一个实例

http://blog.csdn.net/dpsying/article/details/17244759


1,目标

使我们的程序只能有一个正在运行的实例。
并且在第二次运行时,若前一个实例主窗口处于最小化状态,就让前一个实例恢复原大小显示出来。

2. 原理


前一实例进程运行期间得留下一个可供后一实例进程判断的标志,类似进程间通信,可以参考一些IPC的方式。不过这里不需要传递多少数据,仅仅有个标志就OK。
一个简单思路:程序开始时创建一个可命名的内核对象,退出时关闭。如果已存在则说明存在实例在使用这个内核对象。
对于找到前实例主窗口:有一个API,可以给窗口添加一个标志:SetProp.通过遍历桌面的子窗口,用GetProp获取标志,可以判断出我们要的窗口。

3. 实现


①新建MFC对话框应用程序,在app类中加入:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. HANDLE m_handle;  

②在InitInstance()中加入如下代码:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1.     //防止多次实例  
  2.     //用应用程序名创建一个互斥量(任意一种可命名的内核对象都行)  
  3.     m_handle = CreateMutex(NULL, FALSE, m_pszExeName);   
  4.     if (GetLastError() == ERROR_ALREADY_EXISTS)  
  5.     {  
  6.         // 前一实例已存在,退出本实例    
  7.         return FALSE;    
  8.     }  

③给app类添加虚函数ExitInstance(),在程序退出前关闭该内核对象引用。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int CTestApp::ExitInstance()   
  2. {  
  3.     CloseHandle(m_handle);  
  4.     return CWinApp::ExitInstance();  
  5. }  

现在本程序只能运行一次实例了,下面使第二次运行时前实例窗口大小恢复显示。
④在dlg类 OnCreate()中给对话框添加标记:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int CTestDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)   
  2. {  
  3.     if (CDialog::OnCreate(lpCreateStruct) == -1)  
  4.         return -1;  
  5.   
  6.     // 设置窗口标记,用的是应用程序名     
  7.     ::SetProp(m_hWnd, AfxGetApp()->m_pszExeName, (HANDLE)1);  
  8.   
  9.     return 0;  
  10. }  
⑤添加消息响应OnDestroy(),窗口销毁时去掉标记:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void CTestDlg::OnDestroy()   
  2. {  
  3.     CDialog::OnDestroy();  
  4.       
  5.     // TODO: Add your message handler code here  
  6.     // 删除寻找标记     
  7.     ::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName);   
  8. }  

⑥修改app的InitInstance(),使标记的窗口恢复大小:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. BOOL CTestApp::InitInstance()  
  2. {  
  3.     //防止多次实例  
  4.     //用应用程序名创建一个互斥量(任意一种可命名的内核对象都行)  
  5.     m_handle = CreateMutex(NULL, FALSE, m_pszExeName);   
  6.     if (GetLastError() == ERROR_ALREADY_EXISTS)  
  7.     {  
  8.         //寻找先前实例的主窗口    
  9.         //桌面句柄  
  10.         HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(),GW_CHILD);    
  11.         //遍历桌面的子窗口  
  12.         while (::IsWindow(hWndPrevious))   
  13.         {    
  14.             // 检查窗口是否有预设的标记?    
  15.             // 有,则是我们寻找的主窗     
  16.             if (::GetProp(hWndPrevious, m_pszExeName))    
  17.             {     
  18.                 // 主窗口已最小化,则恢复其大小    
  19.                 if (::IsIconic(hWndPrevious))     
  20.                 {  
  21.                     ::ShowWindow(hWndPrevious,SW_RESTORE);     
  22.                     // 将主窗激活     
  23.                     ::SetForegroundWindow(hWndPrevious);      
  24.                     // 将主窗的对话框激活    
  25.                     ::SetForegroundWindow(  ::GetLastActivePopup(hWndPrevious));    
  26.                     // 退出本实例    
  27.                     return FALSE;    
  28.                 }  
  29.             }     
  30.             // 继续寻找下一个窗口     
  31.             hWndPrevious = ::GetWindow(hWndPrevious,GW_HWNDNEXT);    
  32.         }     
  33.         // 前一实例已存在,但没找到其主窗口,可能出错了,退出本实例    
  34.         return FALSE;    
  35.     }  
  36.   
  37.     ……  
  38.   
  39. }  

好了,大功告成!


4. 效果

效果就是没什么效果了……(当第二次运行)但是为了遵循给自己定的五步流程,仍然加上此条~


5. 源码

附上VC6源码:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值