28.VC(custom)-控制一个程序只能有一个进程(单例随手代码)

EnumWindows的使用

BOOL EnumWindows( WNDENUMPROC lpEnumFunc, LPARAMlParam);该函数枚举所有屏幕上的顶层窗口,并将窗口句柄传送给应用程序定义的回调函数,其中lParam是传递给lpEnumFunc的参数。回调函数返回FALSE将停止枚举,否则EnumWindows函数继续到所有顶层窗口枚举完为止。例如获取所有的桌面窗口句柄,并将其值放到CListBox中:

进程统指应用程序的一个实例

两个问题:

1.在程序的第二个实例启动时,如何发现该程序已有 一个实例在运行

2.如何将第一个实例激活,而第二个实例退出

由于windows并没有给内核对象定义唯一的名字标准,而且不同的内核对象共用一个名字空间,可以借鉴定义一个有名字的内核对象来标识唯一(具体不做说明)

 第二个问题的难点是获取第一个实例的主窗对象指针或句柄,然后便可用SetForegroundWindow来激活。虽然FindWindow函数能寻找正运行着的窗口,但该函数要求指明所寻找窗口的标题或窗口类名,不是实现通用方法的途径。我们可以用Win32   SDK函数SetProp来给应用程序主窗设置一个特有的标记。用GetDesktopWindow可以获取Windows系统主控窗口对象指针或句柄,所有应用程序   主窗都可看成该窗口的子窗口,即可用GetWindow函数来获得它们的对象指针或句 柄。用Win32   SDK函数GetProp查找每一应用程序主窗是否包含有我们设置的特定 标记便可确定它是否我们要寻找的第一个实例主窗。使第二个实例退出很简单,只要让其应用程序对InitInstance函数返回FALSE即可。此外,当主窗口退出时 ,应用RemoveProp函数删除我们为其设置的标记。

方法1:

1、在App的InitInstance()中枚举所有窗口,查找本程序实例

HWND oldHWnd = NULL;
EnumWindows(EnumWndProc,(LPARAM)&oldHWnd);    //枚举所有运行的窗口
if(oldHWnd != NULL)
{
	AfxMessageBox("本程序已经在运行了");
	::ShowWindow(oldHWnd,SW_SHOWNORMAL);          //激活找到的前一个程序
	::SetForegroundWindow(oldHWnd);                //把它设为前景窗口
	return false;                                  //退出本次运行
}

2、添加EnumWndProc窗口过程函数:

//添加的标识只运行一次的属性名
CString  g_szPropName = "Your Prop Name";   //自己定义一个属性名
HANDLE   g_hValue = (HANDLE)1;               //自己定义一个属性值

BOOL CALLBACK EnumWndProc(HWND hwnd,LPARAM lParam)
{
HANDLE h = GetProp(hwnd,g_szPropName);
if( h == g_hValue)
{
*(HWND*)lParam = hwnd;
return false;
}
return true;
}

3、在主窗口的 OnInitDialog()中添加属性

<span style="color:#000000;">//设置窗口属性
SetProp(m_hWnd,g_szPropName,g_hValue);

</span>

 

方法二:

BOOL CTestApp::InitInstance() 
{ 
	/********************************/ 
	//   用应用程序名创建信号量  
	HANDLE   hSem   =   CreateSemaphore(NULL, 1, 1, m_pszExeName);   
	//   信号量已存在?  
	//   信号量存在,则程序已有一个实例运行  
	if (GetLastError() == ERROR_ALREADY_EXISTS)   
	{   
		//关闭信号量句柄  
		CloseHandle(hSem);   
		//寻找先前实例的主窗口  
		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;   
	} 
	AfxEnableControlContainer(); 
	//... ... 

} 

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{ 
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1) 
		return -1;

	//设置寻找标记 ljs   

	::SetProp(m_hWnd,   AfxGetApp()->m_pszExeName,(HANDLE)1);   

	m_GameLobbyDlg.Create(this); 
	m_bInit = TRUE; 
	return 0; 
}

CMainFrame::~CMainFrame() 
{ 
	//删除寻找标记  
	::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName);  

} 


方法三:

::CreateMutex(NULL,TRUE,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;    

} 


方法四: 

另建线程创建一个窗口,记录APP的m_hWnd,直接发线程消息,激活这个窗口.

 

 

 

 

 

 

你可能感兴趣的:(windows,null,callback)