Notepad++原始碼閱讀——視窗封裝與繼承

來源:互聯網
上載者:User

標籤:des   style   blog   http   color   os   

引言

近期在看Notepad++的原始碼,學習學習Win32 原生API的開發技巧。

本文以Notepad++ 1.0版本的原始碼為例講解如何封裝windows視窗,實現物件導向開發,如何通過視窗的繼承實現代碼的重用,並且利用C++的動態綁定特性實現多態,另外說明視窗封裝過程中如何封裝訊息處理常式,這是實現物件導向的關鍵所在。聽我細細道來。

實現視窗類別

是Notepad++1.0版本視窗類別的繼承層次:

在Notepad++ 1.0 中所有的視窗元素:編輯視窗、選項卡視窗、工具列、狀態列、對話方塊等等都有一個共同的父類:Window類,該類是一個虛基類,不能被執行個體化,其中的detroy函數是純虛函數。裡面聲明了每個視窗所必須包含的變數:自身的視窗控制代碼_hSelf,父視窗控制代碼 _hParent 和 整個程式的執行個體控制代碼 _hInst。該類實現了一些視窗的基本操作,部分為虛函數。下面我們看看它的原始碼:

#include <windows.h>class Window //虛基類{public:    Window():_hSelf(NULL), _hParent(NULL), _hInst(NULL){};    // 建構函式,在子類中的建構函式調用,為三個變數賦值,    virtual ~Window() {};    virtual void init(HINSTANCE hInst, HWND parent)    // 虛函數、子類中實現自己的版本,如註冊視窗,建立視窗等等    {        _hInst = hInst;        _hParent = parent;    }    virtual void destroy() = 0;    // 資源釋放等等    virtual void display(bool toShow = true) const {// 顯示視窗        ::ShowWindow(_hSelf, toShow?SW_SHOW:SW_HIDE);    };        virtual void reSizeTo(RECT & rc) // should NEVER be const !!!    {                                // 這裡特彆強調rc不能為 const, 因為有時候要通過它返回                                    // 它上面的客戶區,讓客戶上的視窗重設大小。如選項卡視窗                                    // reSizeTo返回選項卡的客戶區、編輯視窗用返回的矩形地區                                    // 重設大小        ::MoveWindow(_hSelf, rc.left, rc.top, rc.right, rc.bottom, TRUE);        redraw();    };    virtual void redraw() const {        // 強制重新整理視窗        ::InvalidateRect(_hSelf, NULL, TRUE);        ::UpdateWindow(_hSelf);    };        virtual void getClientRect(RECT & rc) const {    // 得到使用者區矩形        ::GetClientRect(_hSelf, &rc);    };        virtual int getWidth() const {        RECT rc;        ::GetClientRect(_hSelf, &rc);        return (rc.right - rc.left);    };    virtual int getHeight() const {        RECT rc;        ::GetClientRect(_hSelf, &rc);        return (rc.bottom - rc.top);    };        virtual bool isVisible() const {            return bool(::IsWindowVisible(_hSelf));    };    HWND getHSelf() const {    // 得到自身視窗控制代碼        if (!_hSelf)        {            ::MessageBox(NULL, "_hSelf == NULL", "class Window", MB_OK);            throw int(999);        }        return _hSelf;    };    void getFocus() const {        ::SetFocus(_hSelf);    };    HINSTANCE getHinst() const {        if (!_hInst)        {            ::MessageBox(NULL, "_hInst == NULL", "class Window", MB_OK);            throw int(1999);        }        return _hInst;    };protected:    HINSTANCE _hInst;    // 程式執行個體控制代碼    HWND _hParent;        // 父視窗控制代碼    HWND _hSelf;        // 自身視窗控制代碼};

這就是視窗的基類,用這個基類我們就能派生出自己的實現特定功能的視窗。下面講解幾個典型的視窗。

對話方塊的封裝

Notepad++ 的對話方塊繼承StaticDialog,StaticDialog又繼承上面的Window類。對話方塊基類的聲明如下:

class StaticDialog : public Window{public :    StaticDialog() : Window() {};    ~StaticDialog(){};    virtual void create(int dialogID);    virtual bool isCreated() const {        return reinterpret_cast<bool>(_hSelf);    };    //virtual do        void destroy() {        ::DestroyWindow(_hSelf);    };protected :    static BOOL CALLBACK dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);    virtual BOOL CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) = 0;};
對話方塊的封裝關鍵在於create函數的實現。該函數傳入對話方塊的資源ID然後建立,函數實現如下:

void StaticDialog::create(int dialogID) {    _hSelf = ::CreateDialogParam(_hInst, MAKEINTRESOURCE(dialogID), _hParent,  (DLGPROC)dlgProc, (LPARAM)this);        if (!_hSelf)    {        systemMessage("StaticDialog");        throw int(666);    }    display();}

函數基本就是對話方塊建立的API調用,傳入對話方塊資源、訊息處理常式:dlgProc,這個函數是靜態 static 函數,因此可以傳入該函數調用,最後將this 指標傳入其中,WM_INITDIALOG訊息中可以擷取這個指標。

下面看看dlgProc 的實現:

BOOL CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {    switch (message)     {        case WM_INITDIALOG :        {            StaticDialog *pStaticDlg = (StaticDialog *)(lParam);            pStaticDlg->_hSelf = hwnd;            ::SetWindowLong(hwnd, GWL_USERDATA, (long)lParam);            pStaticDlg->run_dlgProc(message, wParam, lParam);            return TRUE;        }        default :        {            StaticDialog *pStaticDlg = reinterpret_cast<StaticDialog *>(::GetWindowLong(hwnd, GWL_USERDATA));            if (!pStaticDlg)                return FALSE;            return pStaticDlg->run_dlgProc(message, wParam, lParam);        }    }}

在WM_INITDIALOG 訊息中將lParam轉換成StaticDialog指標,這樣就能擷取視窗控制代碼_hSelf(基類成員), 同時將指標放在USERDATA中,在其他訊息中取出,指標並調用成員函數:run_dlgProc,這個函數是純虛函數,繼承的對話方塊子類就能實現自己的特定訊息處理了。這個就是訊息處理常式的封裝。在最後我們還將講解主視窗的訊息處理的封裝,其實和對話方塊所用的方法大同小異。

選項卡視窗

寫累了,待續

主視窗類

寫累了,待續

封裝訊息處理常式(Encapsulating WndProc)

這裡已Notepad++ 1.0 版本的代碼講解如何封裝視窗訊息處理常式。

寫累了,待續

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.