单实例运行程序和SetEventData在非WinCE平台下的实现

转载自: http://blog.csdn.net/qiuchengw/article/details/6049383

 

 

HANDLE   m_hMutex=CreateMutex(NULL,TRUE,   m_pszAppName);     
        if(GetLastError()==ERROR_ALREADY_EXISTS)   {   return   FALSE;   }   

但是本着"折腾"的心态,我想要实现的复杂一点,要不然对不起自己这颗不羁的心. 比如我想添加一个这样的功能:当程序已经运行,再次启动实例的时候就把已经运行的那个实例的窗口提到前端来(假如有GUI窗体).

 

这个也好办:

1>FindWindow --- 这太不专业了,写出来都不好意思给人打招呼

2>CreateEvent(..._T("ShowWindow")) ; 创建一个Event,另开一个线程专门等待第二个激活这个事件,

hEvent = CreateEvent(NULL,FALSE,FALSE,_T("ShowWindow"));// false 参数是必须的,而且不要在本进程的其他地方激活它   
_beginthreadex(WaitEventThread);  
...  
WaitEventThread()  
{  
     while(true)  
     {  
         WaitForSingleObject(hEvent.....);  
         // 下面是想法把UI线程的窗口给提前   
     }  
}  

这方法够专业,但不够简洁.还要另开一个等待线程.

假如我这样想呢:

1>创建系统全局事件,将事件和第一个实例的主窗体句柄关联,

2>启动第二个实例的时候,获取事件关联的窗口句柄,向窗口发送消息激活之.

这样只是用系统提供的消息机制就搞定了,不必另创建线程.  牛逼!有这样的API 吗? MSDN之 , 找到SetEventData,当真感天谢地,立即写代码,代码如下:

class CSingleInstance  
{  
public:  
    CSingleInstance():m_hEvent(NULL){}  
    ~CSingleInstance(){ if (m_hEvent != NULL) {QMUF::CloseHandle(m_hEvent);} }  
    /** 
     * @brief   :    
     * @parma   :   pszName     事件名字 
                bBringLastTop   如果已经有此实例,将其窗口带到前端 
     * @return  :   FALSE       已有实例运行 
     */  
    inline BOOL InstanceAlreadyRun(LPCTSTR pszName,BOOL bBringLastTop=TRUE)  
    {   
        HANDLE hThisEvent = CreateEvent(NULL,TRUE,TRUE,pszName);  
        assert (hThisEvent != NULL);  
        DWORD dwLastInstanceThreadID = GetEventData(hThisEvent);  
        if (dwLastInstanceThreadID > 0)  
        {  
            if (bBringLastTop)  
            {  
                HWND hWnd = GetThreadWindow(dwLastInstanceThreadID);  
                if (hWnd != NULL)  
                {  
                BringWindowToTop(hWnd);  
                }  
            }  
            return TRUE;  
        }  
        m_hEvent = hThisEvent;  
        QMUF::SetEventData(m_hEvent,GetCurrentThreadId());  
        return FALSE;  
    }  
private:  
    HANDLE  m_hEvent;  
};  


 

编译之,激动等待中.....编译错误:没找到SetEventData.我了个去:

SetEventData   Requirements  
OS Versions: Windows CE .NET 4.0 and later.  
Header: Pkfuncs.h.  
Link Library: Coredll.lib.  


 

悲剧XP和Win7...

既然它不给提供,咱就自己实现呗,程序员们又不是吃干饭的.norains已经写了一个实现CMUF(感谢分享):http://blog.csdn.net/norains/archive/2010/03/16/5385429.aspx

但是他的代码有些错误,我又写了一个:QMUF (因为我名字叫秋城),

其中用到 事件/全局内存映射, 想法满简单的,请参考上面的norains的文章,我就不赘述,直接上代码:

#include <iostream>
#include <string>
#include <map> 
#include <windows.h>
#include<iostream>
#include<sstream>
#include <tchar.h>
using namespace std;

class QMUF    
{    
public:  
	struct MemFile    
	{       
		HANDLE hFileMap;    
		HANDLE pMapBuf;  
	};   
	struct ENMF // Event name and associated MemFile    
	{  
		string sEventName;  
		MemFile memFile;  
		bool operator<(const ENMF& enmf)const  
		{  
			return sEventName < enmf.sEventName;  
		}  
	};  
	static DWORD GetEventData(HANDLE hEvent) ;  
	static BOOL SetEventData(HANDLE hEvent,DWORD dwData);  
	static BOOL CloseHandle(HANDLE hObject)  ;  
	static HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,BOOL bInitialState,LPCTSTR lpName)  ;  
private:    
	typedef std::multimap<ENMF, HANDLE>   MapEMH; // ENMF To Handle   
	typedef MapEMH::iterator        EMHItr;  
	typedef std::pair<ENMF,HANDLE>        EMHPair;  
	static MapEMH   ms_mpEMH;  
	static BOOL GetMemFile(LPCTSTR lpEventName,MemFile &memFile) ;  
	static string GetEventName(LPCTSTR lpName,HANDLE hEvent=NULL);  
	static BOOL FindEvent(HANDLE hEvent,EMHItr &itr);  
};    


 

实现:

#include "testQMF.h"


QMUF::MapEMH        QMUF::ms_mpEMH;  
BOOL QMUF::SetEventData(HANDLE hEvent,DWORD dwData)    
{    
	EMHItr itr ;  
	if (FindEvent(hEvent,itr))  
	{  
		MemFile memFile = itr->first.memFile;    
		if(memFile.pMapBuf == NULL)    
		{    
			return FALSE;    
		}    
		//将数值拷贝到内存中     
		memcpy(memFile.pMapBuf,&dwData,sizeof(DWORD));    
	}  
	return FALSE;    
}    
DWORD QMUF::GetEventData(HANDLE hEvent)    
{    
	EMHItr itr ;  
	if (FindEvent(hEvent,itr))  
	{  
		MemFile memFile = itr->first.memFile;    
		if(memFile.pMapBuf == NULL)    
		{    
			return 0;    
		}    
		//从内存中获取DWORD数据   
		DWORD dwVal = 0;    
		memcpy(&dwVal,memFile.pMapBuf,4);    
		return dwVal;  
	}  
	return 0;    
}    
BOOL QMUF::FindEvent(HANDLE hEvent,EMHItr &itr)  
{  
	EMHItr itrEnd = ms_mpEMH.end();  
	for (itr = ms_mpEMH.begin(); itr != itrEnd ; ++itr )  
	{  
		if (itr->second == hEvent)  
		{  
			return TRUE;  
		}  
	}  
	return FALSE;  
}  
BOOL QMUF::CloseHandle(HANDLE hObject)    
{    
	if(hObject == NULL)    
	{    
		return FALSE;    
	}    
	EMHItr itr ;  
	if (FindEvent(hObject,itr))  
	{  
		CloseHandle(itr->first.memFile.hFileMap);  
		ms_mpEMH.erase(itr);  
		return CloseHandle(hObject);  
	}  
	return FALSE;  
}   
BOOL QMUF::GetMemFile(LPCTSTR lpEventName,MemFile &memFile)   
{  
	string sMapName(GetEventName(lpEventName));  
	// 创建映射文件     
	HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,TRUE,sMapName.c_str());  
	if (hFileMap == NULL)  
	{  
		hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,4,  
			sMapName.c_str());    
		if(hFileMap == NULL)   
		{    
			return FALSE;    
		}  
	}  
	//从映射文件句柄获得分配的内存空间     
	VOID *pMapBuf = MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0,0,0);     
	if(pMapBuf == NULL)    
	{    
		CloseHandle(hFileMap);    
		return FALSE;    
	}   
	memFile.hFileMap = hFileMap;    
	memFile.pMapBuf = pMapBuf;    
	return TRUE;    
}  
string QMUF::GetEventName(LPCTSTR lpName,HANDLE hEvent/*=NULL*/)  
{  
	TCHAR buf[MAX_PATH] = {0};  
	//先判断这个类是否只是内部使用。所谓的内部使用,指的是没有名字的事件,除了通过句柄来进行使用以外,无法通过再次打开获得。     
	if(lpName != NULL && _tcslen(lpName) > 0)    
	{    
		//因为内存映射文件和事件名是同一个命名空间,所以这两者的名字不能相同。故我们要创建的内存映射文件名为:EVENT_前缀 + 事件名。     
		_stprintf_s(buf,MAX_PATH,TEXT("EVENT_%s/0"),lpName);  
	}    
	else    
	{    
		//如果该事件为内部使用,那么也就意味着这内存映射文件也是内部使用。故采用程序句柄的名字+事件名的方式进行内存映射文件的名字确定。     
		_stprintf_s(buf,MAX_PATH,TEXT("%ld_%ld/0"),(DWORD)GetModuleHandle(NULL),(DWORD)hEvent);  
	}    
	return buf;  
}  
HANDLE QMUF::CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,  
						 BOOL bInitialState,LPCTSTR lpName)    
{    
	//调用原生的API函数创建事件.     
	HANDLE hEvent = ::CreateEvent(lpEventAttributes,bManualReset,bInitialState,lpName);    
	if(hEvent == NULL)    
	{    
		return NULL;   
	}    
	//先判断当前创建的这个事件是否已经被创建,以方便后续数据的处理。.     
	MemFile memFile;  
	if (!GetMemFile(lpName,memFile))  
	{  
		CloseHandle(hEvent);  
		return NULL;  
	}  
	ENMF enmf = {GetEventName(lpName),memFile};  
	ms_mpEMH.insert(EMHPair(enmf,hEvent));  
	return hEvent;    
}    


 

折腾到这儿了, 单实例运行程序就简单了:

if (m_SingleInst.InstanceAlreadyRun(_T("QQuery")))  
    {  
        return FALSE;  
    }  


 

在任何一个程序启动的时候插入上面三行代码,就完成了单实例的程序运行了,而且还可以定制是否将GUI窗体提到前端来哟!

 

 

另外几个辅助类/函数

void SetForegroundWindowInternal(HWND hWnd)  
    {  
        if(!::IsWindow(hWnd)) return;  
        //relation time of SetForegroundWindow lock   
        DWORD lockTimeOut = 0;  
        HWND  hCurrWnd = ::GetForegroundWindow();  
        DWORD dwThisTID = ::GetCurrentThreadId(),  
            dwCurrTID = ::GetWindowThreadProcessId(hCurrWnd,0);  
        //we need to bypass some limitations from Microsoft :)   
        if(dwThisTID != dwCurrTID)  
        {  
            ::AttachThreadInput(dwThisTID, dwCurrTID, TRUE);  
            ::SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT,0,&lockTimeOut,0);  
            ::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT,0,0,SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);  
            ::AllowSetForegroundWindow(ASFW_ANY);  
        }  
        ::SetForegroundWindow(hWnd);  
        if(dwThisTID != dwCurrTID)  
        {  
            ::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT,0,(PVOID)lockTimeOut,SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);  
            ::AttachThreadInput(dwThisTID, dwCurrTID, FALSE);  
        }  
    }  


 

注意:代码没有经过严格测试. 

 

折腾到这儿,就折腾完了.提裤子走人....

 

你可能感兴趣的:(单实例运行程序和SetEventData在非WinCE平台下的实现)