前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >不定义大量的消息宏实现事件与处理关联起来(避免写很多消息宏定义和消息映射)

不定义大量的消息宏实现事件与处理关联起来(避免写很多消息宏定义和消息映射)

作者头像
大菊观
发布2019-08-14 14:52:49
6380
发布2019-08-14 14:52:49
举报

标题也许不恰当,不必在意。本文主要是记录一种 消息(事件)==》消息响应(事件处理)的映射方式,避免使用大量的消息宏定义。

对于传统的win32窗口、或者mfc窗口、或者duilib窗口等,常见的使用场景比如:

工作线程执行某个函数处理=》处理完毕通知主线程(UI线程)=》主线程收到通知做出对应的处理

在这个过程中,通知主线程通常使用PostMessage或者SendMessage等函数来发送消息的方式。我们需要定义很多的消息宏定义来对消息做区分,以便于主线程收到消息后知道调用哪些消息响应函数。这样我们就需要写很多宏定义,还要给宏定义加注释,还要写对应的映射关系代码,比较累,后续分析代码时也需要跳来跳去有些费劲。

因此本文提供一种方法示例,能够避免写这些消息宏定义,更直观的知道接下来需要调用哪些函数处理。先看代码:

代码语言:javascript
复制
/*这个宏定义可以写到统一公共的头文件里面,避免值重复了
#define WM_MY_MSG_DISPATCHER1				WM_USER + 1001		//自定义消息分发1,多路分发,防止一个阻塞后后续消息无法处理
#define WM_MY_MSG_DISPATCHER2				WM_USER + 1002		//自定义消息分发2
#define WM_MY_MSG_DISPATCHER3				WM_USER + 1003		//自定义消息分发3
#define WM_MY_MSG_DISPATCHER4				WM_USER + 1004		//自定义消息分发4
*/


/***一个自动锁类,单独一个.h文件即可***/
//CKCritSec.h
//
#ifndef __KCRITSEC_H__
#define __KCRITSEC_H__

#include <windows.h>

#pragma warning(disable:4800)

class CKCritSec
{
public:
	CKCritSec() 
	{
		InitializeCriticalSection(&m_CritSec);
	};

    ~CKCritSec() 
	{
		DeleteCriticalSection(&m_CritSec);         
    };

    void Lock() 
	{
		EnterCriticalSection(&m_CritSec);
    };

    void Unlock() 
	{
		LeaveCriticalSection(&m_CritSec);
    };

protected:
		CRITICAL_SECTION    m_CritSec;
};


class CKAutoLock
{
public:
    CKAutoLock(CKCritSec * plock)
    {
        m_pLock = plock;
		if (m_pLock)
		{
			m_pLock->Lock();
		}
    };

    ~CKAutoLock() {
		if (m_pLock)
		{
			m_pLock->Unlock();
		}
    };

protected:
    CKCritSec * m_pLock;
};


class CKEvent
{
public:
    CKEvent(bool bManualReset = false)
	{
		m_hEvent = CreateEvent(NULL, bManualReset, false, NULL);
	};

    virtual ~CKEvent()
	{
		CloseHandle(m_hEvent);
	};

public:
    bool Set(void) 
	{
		bool    bRet = false;

		bRet = ::SetEvent(m_hEvent);
		return bRet;
	};

    bool Wait(unsigned long ulTimeout)
	{
		if (ulTimeout == 0)
			ulTimeout = 0xffffffff;

		bool    bRet = false;

		if(WAIT_OBJECT_0 == WaitForSingleObject(m_hEvent, ulTimeout))
			bRet = true;
		return bRet;
    };

    bool Reset(void) 
	{ 
		bool    bRet = false;

		bRet = ::ResetEvent(m_hEvent);
		return bRet;
	};

protected:
	HANDLE  m_hEvent;
};
#endif



/****消息分发处理,一个MsgDispatch.h文件即可********/
#pragma once

template<class T>
class CMsgDispatch
{
public:
	CMsgDispatch()
	{
	};
	virtual ~CMsgDispatch() 
	{
		CKAutoLock theLock(&m_lockMsg);
		m_dequeMsg.clear();
	};

	typedef void(T::*POnMsgFunc)(WPARAM wParam, LPARAM lParam);
	typedef struct tagMsg
	{
		tagMsg()
		{
			pFunc = NULL;
			wParam = 0;
			lParam = 0;
		};

		tagMsg& operator=(const tagMsg& value)
		{
			pFunc = value.pFunc;
			wParam = value.wParam;
			lParam = value.lParam;
			return *this;
		};
		POnMsgFunc pFunc;
		WPARAM wParam;
		LPARAM lParam;
	}Msg;

public:
	void OnDispatch()
	{
		Msg theMsg;
		while (GetPostMsg(theMsg))
		{
			POnMsgFunc pFunc = theMsg.pFunc;
			(((T*)this)->*pFunc)(theMsg.wParam, theMsg.lParam);
		}
	};
	void PostMsg(HWND hWnd, POnMsgFunc pMsgFunc, WPARAM wParam = 0, LPARAM lParam = 0)
	{
		AddPostMsg(pMsgFunc, wParam, lParam);

		::PostMessage(hWnd, WM_MY_MSG_DISPATCHER1, 0, 0);
		::PostMessage(hWnd, WM_MY_MSG_DISPATCHER2, 0, 0);
		::PostMessage(hWnd, WM_MY_MSG_DISPATCHER3, 0, 0);
		::PostMessage(hWnd, WM_MY_MSG_DISPATCHER4, 0, 0);
	};
	void AddPostMsg(POnMsgFunc pMsgFunc, WPARAM wParam, LPARAM lParam)
	{
		CKAutoLock theLock(&m_lockMsg);
		Msg theMsg;
		theMsg.pFunc = pMsgFunc;
		theMsg.wParam = wParam;
		theMsg.lParam = lParam;

		m_dequeMsg.push_back(theMsg);
	};
	bool GetPostMsg(Msg& theMsg)
	{
		CKAutoLock theLock(&m_lockMsg);
		if (m_dequeMsg.size() > 0)
		{
			theMsg = m_dequeMsg.front();
			m_dequeMsg.pop_front();
			return true;
		}

		return false;
	}
	CKCritSec m_lockMsg;
	deque<Msg> m_dequeMsg;
};

上面是两个.h文件的内容,我写到一起了,一个是封装的互斥锁,一个是消息分派的模版类。这就完成了定义,使用的时候这样(以duilib中的窗口作为示例,其他win32或mfc的窗口原理类似):

代码语言:javascript
复制
class CMainWnd : public CWindowWnd, public INotifyUI, public CMsgDispatch<CMainWnd>
{
public:
	CMainWnd();
	~CMainWnd();

	virtual LPCTSTR GetWindowClassName() const { return _T("CMainWnd"); }
	virtual void    Notify(TNotifyUI& msg);
	virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); 
	virtual LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	UINT GetClassStyle() const { return CS_DBLCLKS; };	//参考360DEMO

public:
	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam);
	LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

	void CloseDlg(WPARAM wParam, LPARAM lParam);
	void OnInitHostSuccess(WPARAM wParam, LPARAM lParam);
	void OnInitHostFailed(WPARAM wParam, LPARAM lParam);

protected:
	CPaintManagerUI m_PM;
};

LRESULT CMainWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT lRes = 0;
	BOOL bHandled = TRUE;

	switch (uMsg)
	{
	case WM_CREATE:						lRes = OnCreate(uMsg, wParam, lParam);							break;
	case WM_NCCALCSIZE:					break;
	case WM_NCACTIVATE:					lRes = 1;														break;
	case WM_NCPAINT:					break;
	case WM_NCHITTEST:					lRes = OnNcHitTest(uMsg, wParam, lParam, bHandled);				break;
	case WM_SIZE:						lRes = OnSize(uMsg, wParam, lParam, bHandled);					break;
	case WM_NCLBUTTONDBLCLK:			bHandled = TRUE;												break;
	case WM_MY_MSG_DISPATCHER1:			OnDispatch();													break;
	case WM_MY_MSG_DISPATCHER2:			OnDispatch();													break;
	case WM_MY_MSG_DISPATCHER3:			OnDispatch();													break;
	case WM_MY_MSG_DISPATCHER4:			OnDispatch();													break;
	default:
		bHandled =FALSE;
	}
	if( bHandled ) return lRes;
	if( m_PM.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;
	return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
}

//其他线程函数中
void InitHostThread(ULONG_PTR lParam)
{
    if (true)
	{
		g_pMainWnd->PostMsg(g_pMainWnd->GetHWND(),&CMainWnd::OnInitHostSuccess);
		return;
	}

	g_pMainWnd->PostMsg(g_pMainWnd->GetHWND(), &CMainWnd::OnInitHostFailed);
}

上述使用主要有两点:1.窗口类要继承这个模板类,模版参数要改为当前的窗口类。2.消息响应函数必须是定义的成员函数指针那样的形式(void返回值,一个WPARAM参数,一个LPARAM参数)。

每一个想要自己添加一些消息映射的窗口,都可以上述方法使用,这样在其他线程想让UI线程来执行某些函数时,直接指定对应的函数即可,不用再做消息宏定义,消息映射等。

对于MFC或者其他的win32窗口等,略微修改即可同样使用。

个人水平有限,欢迎讨论指正,也欢迎提出更好的思路。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年07月31日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档