Duilib 源码分析(二)消息处理

入口函数是_tWinMain

int APIENTRY _tWinMain(
	_In_ HINSTANCE hInstance, 
	_In_opt_ HINSTANCE hPrevInstance, 
	_In_ LPWSTR lpCmdLine, 
	_In_ int nShowCmd)
{
    // 绘制管理器CPaintManagerUI绑定窗口句柄
    CPaintManagerUI::SetInstance(hInstance);    
    // 绘制管理器CPaintManagerUI设置资源目录,用于加载XML
    CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstan
    
    // 创建窗口
    CDuilibWnd duilibWnd;
    duilibWnd.Create(NULL, _T("标题"),UI_WNDSTYLE_FRAME,WS_EX_WINDOWEDGE);
    
    // 显示窗口并且监听消息
    duilibWnd.ShowModal();
    
    return 0;
}

CDuilibWnd 继承 CWindowWnd和INotifyUI

class CDuilibWnd :public CWindowWnd, public INotifyUI
{
    // 绘制管理器:负责绘制界面和管理消息
    CPaintManagerUI m_PaintManager
    
    // CWindowWnd中处理Window消息
    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    // INotifyUI中处理Duilib消息
    void Notify(TNotifyUI& msg);
}

CWindowWnd::Create注册并且创建窗口

// duilib-masterDuiLibCoreUIBase.cpp
HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
    if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
    if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
    m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
    ASSERT(m_hWnd!=NULL);
    return m_hWnd;
}

// 这里就是Win32一样的注册窗口,处理消息的函数是CWindowWnd::__WndProc
bool CWindowWnd::RegisterWindowClass()
{
    WNDCLASS wc = { 0 };
    wc.style = GetClassStyle();
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hIcon = NULL;
    wc.lpfnWndProc = CWindowWnd::__WndProc;
    wc.hInstance = CPaintManagerUI::GetInstance();
    wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = GetWindowClassName();
    ATOM ret = ::RegisterClass(&wc);
    ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS);
    return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
}

// CWindowWnd::__WndProc转发消息给CWindowWnd::HandleMessage
LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CWindowWnd* pThis = NULL;
    ...
.    
    if( pThis != NULL ) {
        return pThis->HandleMessage(uMsg, wParam, lParam);
    } 
    else {
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
}

使用时,先继承CWindowWnd,然后重写HandleMessage
消息先由绘制管理器CPaintManagerUI::MessageHandler 处理

class CDuilibWnd : public CWindowWnd
{
    CPaintManagerUI m_PaintManager; // 绘制管理器
    
    //重写HandleMessage
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
      // 绘制管理器处理消息
      if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes)) 
      {
        return lRes;
      }
   
      // 父类CWindowWnd处理消息
      return __super::HandleMessage(uMsg, wParam, lParam); 
    }
}

CPaintManagerUI::MessageHandler中消息处理的流程
1、私有消息case WM_APP + 1:
  1.1、m_aMessageFilters[i]->MessageHandler
  1.2、pMsg->pSender->OnNotify
  1.3、m_aNotifiers[j]->Notify

//duilib-masterDuiLibCoreUIManager.cpp
bool CPaintManagerUI::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lRes)
{
  // 1、m_aMessageFilters[i]->MessageHandler处理消息
  for( int i = 0; i < m_aMessageFilters.GetSize(); i++ ){
    static_cast<IMessageFilterUI*>(m_aMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled);  
  }
  
  // Notify处理消息(这里处理的是异步消息)
  TNotifyUI* pMsg = NULL;
  while( pMsg = static_cast<TNotifyUI*>(m_aAsyncNotify.GetAt(0)) ) {
  	m_aAsyncNotify.Remove(0);
    // 2、pMsg->pSender->OnNotify处理消息
	if( pMsg->pSender != NULL ) {
		if( pMsg->pSender->OnNotify ) pMsg->pSender->OnNotify(pMsg);
	}
	
    // 3、m_aNotifiers[j]->Notify处理消息
    for( int j = 0; j < m_aNotifiers.GetSize(); j++ ) {
		static_cast<INotifyUI*>(m_aNotifiers[j])->Notify(*pMsg);
	}
	delete pMsg;
  }
}

对应添加消息处理的方法就有

//1、m_aMessageFilters[i]->MessageHandler
//duilib-masterDuiLibCoreUIManager.cpp
bool CPaintManagerUI::AddMessageFilter(IMessageFilterUI* pFilter)
{
    if (pFilter == NULL) return false;
    ASSERT(m_aMessageFilters.Find(pFilter)<0);
    return m_aMessageFilters.Add(pFilter)
}

//2、pMsg->pSender->OnNotify
OnNotify += MakeDelegate(this,&CFrameWindowWnd::OnAlphaChanged);

//3、m_aNotifiers[j]->Notify
//duilib-masterDuiLibCoreUIManager.cpp
bool CPaintManagerUI::AddNotifier(INotifyUI* pNotifier)
{
    if (pNotifier == NULL) return false;
    ASSERT(m_aNotifiers.Find(pNotifier)<0);
    return m_aNotifiers.Add(pNotifie);
}

2、系统Windows消息

// 鼠标左键按下的消息
case WM_LBUTTONDOWN:
{
  POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  CControlUI* pControl = FindControl(pt);
  m_pEventClick = pControl;
  
  TEventUI event = { 0 };
  event.Type = UIEVENT_BUTTONDOWN; // WM_XXX 转成 UIEVENT_XXX
  event.pSender = pControl;
  event.wParam = wParam;
  event.lParam = lParam;
  event.ptMouse = pt;
  event.wKeyState = (WORD)wParam;
  event.dwTimestamp = ::GetTickCount();
  pControl->Event(event);
}

// 鼠标左键弹起的消息
case WM_LBUTTONUP:
{
    POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
    TEventUI event = { 0 };
    event.Type = UIEVENT_BUTTONUP; // WM_XXX 转成 UIEVENT_XXX
    event.pSender = m_pEventClick;
    event.wParam = wParam;
    event.lParam = lParam;
    event.ptMouse = pt;
    event.wKeyState = (WORD)wParam;
    event.dwTimestamp = ::GetTickCount();
	CControlUI* pClick = m_pEventClick;
    m_pEventClick = NULL;
    pClick->Event(event);
}

Duiib的UI事件

//duilib-masterDuiLibCoreUIControl.cpp
void CControlUI::Event(TEventUI& event)
{
    if( OnEvent(&event) ) DoEvent(event);
}

//duilib-masterDuiLibControlUIButton.cpp
void CButtonUI::DoEvent(TEventUI& event)
{
  if( event.Type == UIEVENT_BUTTONDOWN || event.Type == UIEVENT_DBLCLICK )
  {
  	if( ::PtInRect(&m_rcItem, event.ptMouse) && IsEnabled() ) {
  		m_uButtonState |= UISTATE_PUSHED | UISTATE_CAPTURED;
  		Invalidate();
  	}
  	return;
  }
  
  if( event.Type == UIEVENT_BUTTONUP )
  {
  	if( (m_uButtonState & UISTATE_CAPTURED) != 0 ) {
  		if( ::PtInRect(&m_rcItem, event.ptMouse) && IsEnabled()) Activate();
  		m_uButtonState &= ~(UISTATE_PUSHED | UISTATE_CAPTURED);
  		Invalidate();
  	}
  	return;
  }
}

// 按钮被激活,发出消息DUI_MSGTYPE_CLICK
bool CButtonUI::Activate()
{
	if( !CControlUI::Activate() ) return false;
	if( m_pManager != NULL ) m_pManager->SendNotify(this, DUI_MSGTYPE_CLICK); // UIEVENT_XXX 转成  DUI_MSGTYPE_XXX
	return true;
}

Duilib控件发出通知

//duilib-masterDuiLibCoreUIManager.cpp
void CPaintManagerUI::SendNotify(CControlUI* pControl, LPCTSTR pstrMessage, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/, bool bAsync /*= false*/, bool bEnableRepeat /*= true*/)
{
    TNotifyUI Msg;
    Msg.pSender = pControl;
    Msg.sType = pstrMessage;
    Msg.wParam = wParam;
    Msg.lParam = lParam;
    SendNotify(Msg, bAsync, bEnableRepeat);
}

void CPaintManagerUI::SendNotify(TNotifyUI& Msg, bool bAsync /*= false*/, bool bEnableRepeat /*= true*/)
{
  // 同步消息,立马处理
  if( !bAsync ) {
    for( int i = 0; i < m_aNotifiers.GetSize(); i++ )
      static_cast<INotifyUI*>(m_aNotifiers[i])->Notify(Msg);
  }else{
    // 异步消息,暂存起来,下一消息处理
    TNotifyUI *pMsg = new TNotifyUI;
    pMsg->pSender = Msg.pSender;
    pMsg->sType = Msg.sType;
    pMsg->wParam = Msg.wParam;
    pMsg->lParam = Msg.lParam;
    pMsg->ptMouse = Msg.ptMouse;
    pMsg->dwTimestamp = Msg.dwTimestamp;
    m_aAsyncNotify.Add(pMsg);
    PostAsyncNotify();
  }
}

// 自己给自己发消息,用于触发异步消息的处理
void CPaintManagerUI::PostAsyncNotify()
{
	if (!m_bAsyncNotifyPosted) {
                // 给自己发消息WM_APP+1,触发处理异步消息:m_aAsyncNotify
		::PostMessage(m_hWndPaint, WM_APP + 1, 0, 0L);
		m_bAsyncNotifyPosted = true;
	}
}

// 最终由Notify处理Duilib的通知

void Notify(TNotifyUI& msg) 
{
	if (msg.sType == DUI_MSGTYPE_CLICK)
	{
		if (msg.pSender->GetName() == _T("btn"))
		{
			::MessageBox(NULL, _T("按钮内容"), _T("按钮标题"), NULL);
		}
    }
}

  
小结
  使用duilib绘制界面的软件本质还是win32软件,所以还是通过注册窗口过程来接收处理消息。消息的转变过程是WM_XXX -> UIEVENT_XXX -> DUI_MSGTYPE_XXX。最终是由Notify函数来响应duilib界面的操作消息。

  
Duilib技术交流群:799142530
源码地址:https://github.com/KongKong20/DuilibTutor

原文地址:https://www.cnblogs.com/wwgk/p/14344629.html