DuiLib(一)——窗口及消息

最近看了下开源界面库duilib的代码,写几篇相关的文章。网上已经有好多相关的文章了,我这里只是记录自己的学习过程,写到哪里算哪里权当自娱自乐

duilib是一轻量级的direcui界面库,所谓directui是指在一真实的窗口之上画出各种控件。所以先从界面库的窗口及消息入手比较好,可以抓住树根,再顺着往上分析。

duilib将窗口封装成类CWindowWnd,创建窗口之前要先注册窗口:

 1 bool CWindowWnd::RegisterWindowClass()
 2 {
 3     WNDCLASS wc = { 0 };
 4     wc.style = GetClassStyle();
 5     wc.cbClsExtra = 0;
 6     wc.cbWndExtra = 0;
 7     wc.hIcon = NULL;
 8     wc.lpfnWndProc = CWindowWnd::__WndProc;//窗口过程
 9     wc.hInstance = CPaintManagerUI::GetInstance();
10     wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
11     wc.hbrBackground = NULL;
12     wc.lpszMenuName  = NULL;
13     wc.lpszClassName = GetWindowClassName();//窗口类名
14     ATOM ret = ::RegisterClass(&wc);
15     ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS);
16     return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
17 }
1 HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
2 {
3     if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
4     if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
5     m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
6     ASSERT(m_hWnd!=NULL);
7     return m_hWnd;
8 }

窗口过程为CWindowWnd类的静态函数__WndProc

 1 LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 2 {
 3     CWindowWnd* pThis = NULL;
 4     if( uMsg == WM_NCCREATE ) {//before WM_CREATE
 5         LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
 6         pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
 7         pThis->m_hWnd = hWnd;
 8         ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
 9     } 
10     else {
11         pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
12         if( uMsg == WM_NCDESTROY && pThis != NULL ) {//after WM_DESTROY
13             LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);
14             ::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);
15             if( pThis->m_bSubclassed ) pThis->Unsubclass();
16             pThis->m_hWnd = NULL;
17             pThis->OnFinalMessage(hWnd);
18             return lRes;
19         }
20     }
21     if( pThis != NULL ) {
22         //消息处理
23         return pThis->HandleMessage(uMsg, wParam, lParam);
24     } 
25     else {
26         return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
27     }
28 }

窗口函数__WndProc:

  • 处理了WM_NCCREATE消息(WM_CREATE之前发送)和WM_NCDESTROY(WM_DESTROY之后发送)。前者保存了CWindowWnd对象指针,后者获取该指针,并调用虚函数OnFinalMessage,给用户一个最后清理的机会。
  • 调用虚函数HandleMessage,处理其他消息

 消息循环在哪?不在类CWindowWnd中,在CPaintManagerUI里!

void CPaintManagerUI::MessageLoop()
{
    MSG msg = { 0 };
    while( ::GetMessage(&msg, NULL, 0, 0) ) {
        if( !CPaintManagerUI::TranslateMessage(&msg) ) {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
        }
    }
}

一个简单的示例:

class CFrameWindowWnd : public CWindowWnd, public INotifyUI
{
public:
    CFrameWindowWnd() { };
    LPCTSTR GetWindowClassName() const { return _T("FrameWnd"); };
    UINT GetClassStyle() const { return UI_CLASSSTYLE_FRAME | CS_DBLCLKS; };
    void OnFinalMessage(HWND /*hWnd*/) { delete this; };

    void Notify(TNotifyUI& msg)
    {
        if( msg.sType == _T("windowinit") ) {
        }
        else if( msg.sType == _T("click") ) {
        }
    }

    //消息处理:窗口函数__WndProc ---> HandleMessage
    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if( uMsg == WM_CREATE ) {
            m_pm.Init(m_hWnd);
            //根据XML创建控件
            CDialogBuilder builder;
            CControlUI* pRoot = builder.Create(_T("HelloWorld.xml"), (UINT)0, NULL, &m_pm);
            ASSERT(pRoot && "Failed to parse XML");
            m_pm.AttachDialog(pRoot);
            m_pm.AddNotifier(this);
            return 0;
        }
        else if( uMsg == WM_DESTROY ) {
            ::PostQuitMessage(0L);
        }
        else if( uMsg == WM_ERASEBKGND ) {
            return 1;
        }

        //消息处理:CPaintManagerUI::MessageHandler
        LRESULT lRes = 0;
        if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes;
        return CWindowWnd::HandleMessage(uMsg, wParam, lParam);
    }

public:
    CPaintManagerUI m_pm;
};


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int nCmdShow)
{
    CPaintManagerUI::SetInstance(hInstance);
    CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath() + _T("skin\HelloWorldRes"));

    //COM
    HRESULT Hr = ::CoInitialize(NULL);
    if( FAILED(Hr) ) return 0;

    CFrameWindowWnd* pFrame = new CFrameWindowWnd();
    if( pFrame == NULL ) return 0;

    //注册窗口类、创建窗口
    pFrame->Create(NULL, _T("HelloWorld"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
    pFrame->CenterWindow();
    pFrame->ShowWindow(true);

    //消息循环
    CPaintManagerUI::MessageLoop();

    //COM
    ::CoUninitialize();
    return 0;
}

这个例子可以看到整个程序框架,注册、创建窗口、消息循环、消息处理等。

关于窗口和消息,先写这么多。下一篇写控件创建

原文地址:https://www.cnblogs.com/dahai/p/3455727.html