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

 

单实例运行程序的方法蛮多(google知道有哪些).用VC的话,这活就太简单了,下面简单两行代码搞定

 

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的文章,我就不赘述,直接上代码:

class QMUF { public: struct MemFile { HANDLE hFileMap; HANDLE pMapBuf; }; struct ENMF // Event name and associated MemFile { tstring 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 tstring GetEventName(LPCTSTR lpName,HANDLE hEvent=NULL); static BOOL FindEvent(HANDLE hEvent,EMHItr &itr); };  

实现:

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) { tstring 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; } tstring 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); } } 


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

 

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

 

 

你可能感兴趣的:(struct,Microsoft,null,平台,WinCE,attributes)