• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            旅途

            如果想飛得高,就該把地平線忘掉

            走出MFC子類化的迷宮

            走出MFC子類化的迷宮

            KEY WORDS:子類化 SUBCLASSWINDOW  MFC消息機(jī)制

             

            許多Windows程序員都是跳過(guò)SDK直接進(jìn)行RAD開發(fā)工具[VC,我想VC應(yīng)不屬于RAD]的學(xué)習(xí),有些人可能對(duì)子類化機(jī)制比較陌生。

            我們先看看什么是Windows的子類化。Windows給我們或是說(shuō)給它自己定義了許多豐富的通用控件,如:EditComboBox ListBox……等,這些控件功能豐富,能為我們開發(fā)工作帶來(lái)極大方面,試想:我們單單是自己實(shí)現(xiàn)一個(gè)EDIT控件是多么的艱難!但是,在實(shí)際開發(fā)中還是有些情況這些標(biāo)準(zhǔn)控件也無(wú)能為力,比如:在我們的應(yīng)用中要求一個(gè)EDIT得到老師對(duì)學(xué)生的評(píng)價(jià)ABC[不要對(duì)我說(shuō)你想用ComboBox實(shí)現(xiàn)J],這時(shí),要求在Edit中禁止對(duì)其它字母、數(shù)字的輸入操作,怎么辦?EDIT控件本身沒有提供這種機(jī)制,我們就可以采用子類化很好的解決這類問(wèn)題。

            我們知道,每一個(gè)Windows窗口[這里是EDIT]都有一個(gè)窗口處理函數(shù)負(fù)責(zé)對(duì)消息處理,子類化的辦法就是用我們自己的消息處理函數(shù)來(lái)替代窗口原有的、標(biāo)準(zhǔn)的處理函數(shù)。當(dāng)然我們自己的窗口處理函數(shù)只是關(guān)心那些特定的消息[在這里當(dāng)然是WM_CHAR],而其它消息,再發(fā)給原來(lái)的窗口函數(shù)處理。在SDK中的實(shí)現(xiàn)方法是調(diào)用函數(shù)SetWindowLong

            WNDPROC * oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)AfxGetAfxWndProc());

            其中AfxGetAfxWndProc()是我們自己的窗口處理函數(shù),在其中處理過(guò)我們感興趣的消息后就可能通過(guò)返回的原窗口處理函數(shù)指針oldWndProc來(lái)把其它消息按標(biāo)準(zhǔn)方法處理掉,具體做法請(qǐng)查閱相關(guān)資料。

            但到了MFC“時(shí)代”,一切都被包裝起來(lái)了,原來(lái)的窗口類注冊(cè)、窗口函數(shù)都不見了[或是說(shuō)隱身了],我想對(duì)于那些“刨根問(wèn)底”的程序員有興趣了解在MFC中的子類化機(jī)制,本人就自己做的一點(diǎn)“探索”作出總結(jié),希望能給大家點(diǎn)啟示。

            我們先用MFC實(shí)現(xiàn)我上面提到的要求:一個(gè)只能輸入ABCEDIT控件。

            啟動(dòng)時(shí)界面如下:

            輸入時(shí)就只能輸入ABC,并且只允許輸入一個(gè)字母。

            實(shí)現(xiàn)方法:

            先派生一個(gè)自己的類CsuperEditCtrl + W后,在其中處理WM_CHAR,然后再編輯這個(gè)消息處理函數(shù):

             

            void CSuperEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)

            {

                 // TODO: Add your message handler code here and/or call default

                 TCHAR ch[20];

                 GetWindowText(ch,20);

                 if (strlen(ch) == 1 && (nChar <= 'C' && nChar >= 'A'))

                        return;

                 if (nChar != 'A'

                        && nChar != 'B'

                        && nChar != 'C'

                        )

                        return;

                

                 CEdit::OnChar(nChar, nRepCnt, nFlags);

            }

             

            然后再給我們Cprog1Dlg類中加入一個(gè)數(shù)據(jù)成員CsuperEdit m_edit,在CProg1Dlg::OnInitDialog()中加入:

            m_edit.SubclassDlgItem(IDC_EDIT1,this);

                 m_edit.SetWindowText("<請(qǐng)輸入ABC>");

            并處理EDITDIALOG發(fā)送的通知消息:EN_SETFOCUS

            void CProg1Dlg::OnSetfocusEdit1()

            {

                 // TODO: Add your control notification handler code here

                 m_edit.SetWindowText("");

                 m_edit.SetFocus();

            }

             

            OK,一切搞定!和SDK的子類化方法比起來(lái),這是多么的容易!

            我們看看MFC背著我們到底做了什么!這里主要解決兩個(gè)容易讓初學(xué)者比較疑惑的問(wèn)題:

            1、    m_edit只是我們定義的一個(gè)C++類對(duì)象,為什么通過(guò)它調(diào)用其成員函數(shù)SetWindowText便可以控制我們程序中資源編號(hào)為:IDC_EDIT1的控件?

            2、    CSuperEdit類為什么可以處理WM_CHAR消息?

             

            大家都知道,控制Windows窗口、控件、資源……都是通過(guò)它們的句柄來(lái)實(shí)現(xiàn),如
            HHANDLE
            HWNDHDC都是句柄,它表現(xiàn)為一個(gè)32位長(zhǎng)整形數(shù)據(jù),存放于Windows中的特定區(qū)域,我們可以把它理解為指向我們想控制的窗口、控件、資源的索引,有了它,我們就可以控制我們想要控制的對(duì)象

            這里你可以想到為什么多數(shù)API函數(shù)都有一個(gè)參數(shù)HWND hwnd了吧!

            BOOL SetWindowText(
              HWND hWnd,         // handle to window or control
              LPCTSTR lpString   // title or text
            );

            我們的C++變量m_edit要想控制IDC_EDIT1,也要通過(guò)它的句柄,但這又是如何實(shí)現(xiàn)的呢?您可能注意到了m_edit.SubclassDlgItem(IDC_EDIT1,this);一句,對(duì)了,這就是關(guān)鍵所在!

            在此處F9設(shè)置斷點(diǎn),F5之后,程序到達(dá)此處,F11跟入SubclassDlgItem函數(shù):

            BOOL CWnd::SubclassDlgItem(UINT nID, CWnd* pParent)

            {

                 ASSERT(pParent != NULL);

                 ASSERT(::IsWindow(pParent->m_hWnd));

             

                 // check for normal dialog control first

                 HWND hWndControl = ::GetDlgItem(pParent->m_hWnd, nID);

                 if (hWndControl != NULL)

                        return SubclassWindow(hWndControl);

             

            #ifndef _AFX_NO_OCC_SUPPORT

                 if (pParent->m_pCtrlCont != NULL)

                 {

                        // normal dialog control not found

                        COleControlSite* pSite = pParent->m_pCtrlCont->FindItem(nID);

                        if (pSite != NULL)

                        {

                               ASSERT(pSite->m_hWnd != NULL);

                               VERIFY(SubclassWindow(pSite->m_hWnd));

             

            #ifndef _AFX_NO_OCC_SUPPORT

                               // If the control has reparented itself (e.g., invisible control),

                               // make sure that the CWnd gets properly wired to its control site.

                               if (pParent->m_hWnd != ::GetParent(pSite->m_hWnd))

                                      AttachControlSite(pParent);

            #endif //!_AFX_NO_OCC_SUPPORT

             

                               return TRUE;

                        }

                 }

            #endif

             

                 return FALSE;   // control not found

            }

            代碼開始時(shí)對(duì)傳入的父窗口做些檢查,然后就是

            HWND hWndControl = ::GetDlgItem(pParent->m_hWnd, nID);

                 if (hWndControl != NULL)

                        return SubclassWindow(hWndControl);

            這是關(guān)鍵的代碼,先用hWndControl得到我們IDC_EDIT1控件的句柄,然后調(diào)用

            SubclassWindow函數(shù),這個(gè)函數(shù)是實(shí)現(xiàn)的關(guān)鍵,我們來(lái)看一下它做了什么:

             

             

             

            BOOL CWnd::SubclassWindow(HWND hWnd)

            {

                 if (!Attach(hWnd))

                        return FALSE;

             

                 // allow any other subclassing to occur

                 PreSubclassWindow();

             

                 // now hook into the AFX WndProc

                 WNDPROC* lplpfn = GetSuperWndProcAddr();

                 WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,     (DWORD)AfxGetAfxWndProc());

                 ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());

             

                 if (*lplpfn == NULL)

                        *lplpfn = oldWndProc;   // the first control of that type created

            #ifdef _DEBUG

                 else if (*lplpfn != oldWndProc)

                 {

                        TRACE0("Error: Trying to use SubclassWindow with incorrect CWnd\n");

                        TRACE0("\tderived class.\n");

                        TRACE3("\thWnd = $%04X (nIDC=$%04X) is not a %hs.\n", (UINT)hWnd,

                               _AfxGetDlgCtrlID(hWnd), GetRuntimeClass()->m_lpszClassName);

                        ASSERT(FALSE);

                        // undo the subclassing if continuing after assert

                        ::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc);

                 }

            #endif

             

                 return TRUE;

            }

             

            函數(shù)Attach內(nèi)部如下:

            BOOL CWnd::Attach(HWND hWndNew)

            {

                   ASSERT(m_hWnd == NULL);     // only attach once, detach on destroy

                   ASSERT(FromHandlePermanent(hWndNew) == NULL);

                          // must not already be in permanent map

             

                   if (hWndNew == NULL)

                          return FALSE;

             

                   CHandleMap* pMap = afxMapHWND(TRUE); // create map if not exist

                   ASSERT(pMap != NULL);

             

                   pMap->SetPermanent(m_hWnd = hWndNew, this);

             

            #ifndef _AFX_NO_OCC_SUPPORT

                   AttachControlSite(pMap);

            #endif

             

                   return TRUE;

            }

             

            這里要說(shuō)明的是pMap->SetPermanent(m_hWnd = hWndNew, this);一句,它把我們IDC_EDIT1的句柄賦值給類CsuperEdit的數(shù)據(jù)成員m_hWnd [別忘了我們的CsuperEdit類是派生于Cedit],大家可能現(xiàn)在已經(jīng)隱約的明白了些什么,不錯(cuò),在m_edit.SetWindowText("<請(qǐng)輸入ABC>");中正是通過(guò)這個(gè)數(shù)據(jù)成員m_hWnd實(shí)現(xiàn)對(duì)IDC_EDIT1控制的:

            void CWnd::SetWindowText(LPCTSTR lpszString)

            {

                   ASSERT(::IsWindow(m_hWnd));

             

                   if (m_pCtrlSite == NULL)

                          ::SetWindowText(m_hWnd, lpszString);

                   else

                          m_pCtrlSite->SetWindowText(lpszString);

            }

            其它CEdit類的函數(shù)也都是圍繞 m_hWnd + API函數(shù)” 進(jìn)行包裝的。

            而我們常用的DDX_Control方法說(shuō)到底也是調(diào)用SubclassWindow

             

            怎么樣?第一個(gè)問(wèn)題的來(lái)龍去脈搞明白了吧?

             

            現(xiàn)在看看第二個(gè)問(wèn)題:CSuperEdit類為什么可以處理WM_CHAR消息?

            可能有的朋友現(xiàn)在疑惑,雖然通過(guò)句柄實(shí)現(xiàn)了m_edit對(duì)IDC_EDIT的控制,但發(fā)送給它的消息照樣跑到EDIT的標(biāo)準(zhǔn)處理函數(shù)中,對(duì)WM_CHAR的處理是如何實(shí)現(xiàn)的呢?

            如果消息照樣跑到EDIT的標(biāo)準(zhǔn)處理函數(shù)中,那當(dāng)然是不能處理了!不知您有沒有看到在上面的SubclassWindow函數(shù)中有這么一小段我加了重點(diǎn)標(biāo)示:

            // now hook into the AFX WndProc

                 WNDPROC* lplpfn = GetSuperWndProcAddr();

                 WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,     (DWORD)AfxGetAfxWndProc());

                 ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());

             

                 if (*lplpfn == NULL)

                        *lplpfn = oldWndProc;   // the first control of that type created

            再和我們開始講到的SDK中子類化機(jī)制聯(lián)系起來(lái),明白了吧?MFC在這里神不知鬼不覺的搞起偷天換日的勾當(dāng)!

            這個(gè)AfxGetAfxWndProc()函數(shù)是這樣的:

             

             

            WNDPROC AFXAPI AfxGetAfxWndProc()

            {

            #ifdef _AFXDLL

                   return AfxGetModuleState()->m_pfnAfxWndProc;

            #else

                   return &AfxWndProc;

            #endif

            }

            讀過(guò)侯捷先生《深入淺出MFC》的朋友不知還是否記得MFC的命令路由機(jī)制正是以這個(gè)函數(shù)為起點(diǎn)的!

            這樣當(dāng)程序收到發(fā)給EditWM_CHAR時(shí),本應(yīng)調(diào)用EDIT標(biāo)準(zhǔn)窗口處理函數(shù),現(xiàn)在被改為調(diào)用LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)了,然后WM_CHAR消息進(jìn)行一系列的流竄,最終成功到達(dá)我們的處理函數(shù)CSuperEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags),至于是如何流竄的、怎么到達(dá)的請(qǐng)參考《深入淺出MFC[如果您的書是繁體電子版,請(qǐng)從566頁(yè)讀起]

             

            終于,我們走出了FMC子類化的迷宮。

             

            posted on 2007-07-17 23:42 旅途 閱讀(213) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 深入windows

            久久亚洲国产精品成人AV秋霞| 亚洲国产精品久久电影欧美| 亚洲国产欧美国产综合久久| 青春久久| 九九久久自然熟的香蕉图片| 日本久久久久久中文字幕| 内射无码专区久久亚洲| 人妻精品久久无码区| 亚洲精品无码久久毛片| 狠狠色丁香婷婷综合久久来| 久久久久久精品久久久久| 久久久精品国产亚洲成人满18免费网站 | 精品久久久久久久久久中文字幕| 亚洲综合久久综合激情久久| 久久久久久综合网天天| 777米奇久久最新地址| 精品国产乱码久久久久软件| 国产亚洲精品美女久久久| 青青热久久国产久精品| 久久影院综合精品| 久久午夜无码鲁丝片秋霞| 久久99免费视频| 国产午夜精品久久久久免费视| 国产精品成人久久久| 久久精品一区二区三区中文字幕| 精品一区二区久久| 国产精品美女久久久久| 色综合久久综合中文综合网| 久久热这里只有精品在线观看| 一本色道久久88综合日韩精品| 久久久久人妻一区精品| 狠狠精品久久久无码中文字幕 | 亚洲国产精久久久久久久| 日韩AV无码久久一区二区| 亚洲欧美成人综合久久久 | 99久久99这里只有免费的精品| 亚洲狠狠婷婷综合久久久久| 久久精品国产亚洲av日韩| 91亚洲国产成人久久精品| 一本一道久久精品综合| 国产国产成人久久精品|