逆枫゛

Qt学习群:1149411109 群文件提供博客源码,定期答疑、更新学习资料。

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1,目标

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

2,原理


前一实例进程运行期间得留下一个可供后一实例进程判断的标志,类似进程间通信,可以参考一些IPC的方式。不过这里不需要传递多少数据,仅仅有个标志就OK。
一个简单思路:程序开始时创建一个可命名的内核对象,退出时关闭。如果已存在则说明存在实例在使用这个内核对象。

对于找到前实例主窗口:有一个API,可以给窗口添加一个标志:SetProp.通过遍历桌面的子窗口,用GetProp获取标志,可以判断出我们要的窗口。

3,实现


①新建MFC对话框应用程序,在app类中加入:
HANDLE m_handle;

②在InitInstance()中加入如下代码:
//防止多次实例
	//用应用程序名创建一个互斥量(任意一种可命名的内核对象都行)
	m_handle = CreateMutex(NULL, FALSE, m_pszExeName); 
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		// 前一实例已存在,退出本实例  
		return FALSE;  
 	}

③给app类添加虚函数ExitInstance(),在程序退出前关闭该内核对象引用。
int CTestApp::ExitInstance() 
{
	CloseHandle(m_handle);
	return CWinApp::ExitInstance();
}

现在本程序只能运行一次实例了,下面使第二次运行时前实例窗口大小恢复显示。
④在dlg类 OnCreate()中给对话框添加标记:
int CTestDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CDialog::OnCreate(lpCreateStruct) == -1)
		return -1;

	// 设置窗口标记,用的是应用程序名   
	::SetProp(m_hWnd, AfxGetApp()->m_pszExeName, (HANDLE)1);

	return 0;
}
⑤添加消息响应OnDestroy(),窗口销毁时去掉标记:
void CTestDlg::OnDestroy() 
{
	CDialog::OnDestroy();
	
	// TODO: Add your message handler code here
	// 删除寻找标记   
	::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName); 
}

⑥修改app的InitInstance(),使标记的窗口恢复大小:
BOOL CTestApp::InitInstance()
{
	//防止多次实例
	//用应用程序名创建一个互斥量(任意一种可命名的内核对象都行)
	m_handle = CreateMutex(NULL, FALSE, m_pszExeName); 
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		//寻找先前实例的主窗口  
		//桌面句柄
		HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(),GW_CHILD);  
		//遍历桌面的子窗口
		while (::IsWindow(hWndPrevious)) 
		{  
			// 检查窗口是否有预设的标记?  
			// 有,则是我们寻找的主窗   
			if (::GetProp(hWndPrevious, m_pszExeName))  
			{   
				// 主窗口已最小化,则恢复其大小  
				if (::IsIconic(hWndPrevious))   
				{
					::ShowWindow(hWndPrevious,SW_RESTORE);   
					// 将主窗激活   
					::SetForegroundWindow(hWndPrevious);    
					// 将主窗的对话框激活  
					::SetForegroundWindow(  ::GetLastActivePopup(hWndPrevious));  
					// 退出本实例  
					return FALSE;  
				}
			}   
			// 继续寻找下一个窗口   
			hWndPrevious = ::GetWindow(hWndPrevious,GW_HWNDNEXT);  
		}   
		// 前一实例已存在,但没找到其主窗口,可能出错了,退出本实例  
		return FALSE;  
 	}

	……

}

好了,大功告成!


4,效果

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


5,源码

附上VC6源码:


posted on 2013-12-10 16:04  逆枫゛  阅读(91)  评论(0编辑  收藏  举报