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;

}

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

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

你可能感兴趣的:(lib)