日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國(guó)內(nèi)最全I(xiàn)T社區(qū)平臺(tái) 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁(yè) > php開(kāi)源 > php教程 > [置頂] 修復(fù)duilib CEditUI控件和CWebBrowserUI控件中按Tab鍵無(wú)法切換焦點(diǎn)的bug

[置頂] 修復(fù)duilib CEditUI控件和CWebBrowserUI控件中按Tab鍵無(wú)法切換焦點(diǎn)的bug

來(lái)源:程序員人生   發(fā)布時(shí)間:2014-12-14 08:37:32 閱讀次數(shù):8568次

轉(zhuǎn)載請(qǐng)說(shuō)明原出處,謝謝~~:http://blog.csdn.net/zhuhongshu/article/details/41556615


        在duilib中,按tab鍵會(huì)讓焦點(diǎn)在Button1類(lèi)的控件中切換,但是切換焦點(diǎn)1直存在bug,具體的描寫(xiě)以下:

        1、在主窗體里彈出新的窗體,當(dāng)新窗體中存在CEditUI控件并且焦點(diǎn)在此CEditUI控件上,那末按tab鍵將沒(méi)法切換焦點(diǎn)而1直處于CEditUI中。(只在新窗體中有此bug,主創(chuàng)體中沒(méi)有,緣由會(huì)在后面分析)

        2、CWebBrowserUI控件同CEditUI


        之間在群里就看到有人問(wèn)這個(gè)問(wèn)題,而且也1直沒(méi)解決。


       這幾天在用duilib寫(xiě)1個(gè)注冊(cè)界面時(shí)(如圖,此頁(yè)面便是在主窗體上面的1個(gè)彈出窗體),上面有多個(gè)CEditUI控件,依照我們的習(xí)慣,輸入完第1個(gè)edit的內(nèi)容后會(huì)按tab切換到下1個(gè)edit。而由于duilib的bug致使這個(gè)焦點(diǎn)沒(méi)法切換。我自己1般是需要甚么功能就摸索甚么功能,之前用duilib是沒(méi)有遇到edit切換焦點(diǎn)的需求,所以就沒(méi)有斟酌過(guò)這個(gè)bug,今天碰到了這個(gè)需求,就得先解決這個(gè)bug了。

       



分析進(jìn)程1:


        很明顯可以看出來(lái),這個(gè)bug只存在于CEditUI和CWebBrowserUI控件中,而這兩個(gè)控件與其他控件的區(qū)分就在于他們都是用了原生的wini32控件,我這里就只分析CEditUI控件了。


        在CEditUI控件的源碼里可以很容易看到,當(dāng)他的DoEvent函數(shù)里收到獲得焦點(diǎn)的UIEVENT_SETFOCUS消息或鼠標(biāo)按下的UIEVENT_BUTTONDOWN消息后,他就會(huì)創(chuàng)建1個(gè)子窗體并且保護(hù)這個(gè)子窗體的相干數(shù)據(jù)。而這個(gè)子窗體會(huì)自動(dòng)通過(guò)CreateWindowEx函數(shù)創(chuàng)建1個(gè)原生的win32的edit控件,當(dāng)子窗體失去焦點(diǎn)時(shí)自動(dòng)燒毀本身,這也就是CEditUI控件的實(shí)現(xiàn)原理。


       焦點(diǎn)切換的處理是由CPaintManager類(lèi)管理的,當(dāng)我們?cè)诮缑嬷邪聪耇ab鍵打算切換焦點(diǎn)后,CPaintManager會(huì)攔截鍵盤(pán)消息然后去管理焦點(diǎn)切換,那末我修復(fù)出發(fā)點(diǎn)就從焦點(diǎn)管理函數(shù)開(kāi)始。焦點(diǎn)管理的函數(shù)是PreMessageHandler,原型以下:


bool CPaintManagerUI::PreMessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& /*lRes*/) { for( int i = 0; i < m_aPreMessageFilters.GetSize(); i++ ) { bool bHandled = false; LRESULT lResult = static_cast<IMessageFilterUI*>(m_aPreMessageFilters[i])->MessageHandler(uMsg, wParam, lParam, bHandled); if( bHandled ) { return true; } } switch( uMsg ) { case WM_KEYDOWN: { // Tabbing between controls if( wParam == VK_TAB ) { if( m_pFocus && m_pFocus->IsVisible() && m_pFocus->IsEnabled() && _tcsstr(m_pFocus->GetClass(), _T("RichEditUI")) != NULL ) { if( static_cast<CRichEditUI*>(m_pFocus)->IsWantTab() ) return false; } SetNextTabControl(::GetKeyState(VK_SHIFT) >= 0); return true; } } break; //....省略無(wú)用代碼 }


        可以看到函數(shù)里接活VK_TAB按鍵后,會(huì)去調(diào)用SetNtextTabControl函數(shù)去設(shè)置下1個(gè)控件獲得焦點(diǎn),然后返回true。而SetNtextTabControl函數(shù)的原型以下:


bool CPaintManagerUI::SetNextTabControl(bool bForward) { // If we're in the process of restructuring the layout we can delay the // focus calulation until the next repaint. if( m_bUpdateNeeded && bForward ) { m_bFocusNeeded = true; ::InvalidateRect(m_hWndPaint, NULL, FALSE); return true; } // Find next/previous tabbable control FINDTABINFO info1 = { 0 }; info1.pFocus = m_pFocus; info1.bForward = bForward; CControlUI* pControl = m_pRoot->FindControl(__FindControlFromTab, &info1, UIFIND_VISIBLE | UIFIND_ENABLED | UIFIND_ME_FIRST); if( pControl == NULL ) { if( bForward ) { // Wrap around FINDTABINFO info2 = { 0 }; info2.pFocus = bForward ? NULL : info1.pLast; info2.bForward = bForward; pControl = m_pRoot->FindControl(__FindControlFromTab, &info2, UIFIND_VISIBLE | UIFIND_ENABLED | UIFIND_ME_FIRST); } else { pControl = info1.pLast; } } if( pControl != NULL ) SetFocus(pControl); m_bFocusNeeded = false; return true; }

       函數(shù)里調(diào)用FindControl函數(shù),根據(jù)__FindControlFromTab函數(shù)和bForward參數(shù)來(lái)決定搜索下1個(gè)焦點(diǎn)的控件,__FindControlFromTab函數(shù)的代碼我就不分析了,當(dāng)找到了下1個(gè)應(yīng)當(dāng)獲得焦點(diǎn)的控件后,調(diào)用CPaintManager的SetFocus函數(shù)讓新控件獲得焦點(diǎn)。而SetFocus函數(shù)里,首先對(duì)舊的獲得焦點(diǎn)的控件發(fā)送UIEVENT_KILLFOCUS消息讓他失去焦點(diǎn),然后將新的獲得焦點(diǎn)的控件指針賦值給m_pFocus變量(CPaintManager中保存當(dāng)前獲得焦點(diǎn)的控件指針的成員變量),并且給新的獲得焦點(diǎn)的控件發(fā)送UIEVENT_SETFOCUS消息讓他獲得焦點(diǎn)。


      從代碼中看,理論上沒(méi)有甚么問(wèn)題,我就針對(duì)CEditUI來(lái)進(jìn)行修改。在CEditUI的內(nèi)嵌子窗體類(lèi)CEditWnd中的HandleMessage函數(shù)里加入以下代碼,讓CEditWnd收到Tab消息后來(lái)主動(dòng)調(diào)用CPaintManager的SetNextTabControl函數(shù)來(lái)切換焦點(diǎn):


else if( uMsg == WM_CHAR ){ if(TCHAR(wParam) == VK_TAB) { m_pOwner->GetManager()->SetNextTabControl(::GetKeyState(VK_SHIFT) >= 0); } else bHandled = FALSE; }


      這樣修改后還不起作用,緣由是PreMessageHandler函數(shù)中處理WM_KEYDOWN消息后直接reutrn true致使了消息的截?cái)?,從而沒(méi)法傳遞到CEditWnd,所以再把return true語(yǔ)句注釋掉,這時(shí)候會(huì)欣喜的發(fā)現(xiàn),可以切換焦點(diǎn)了!


分析進(jìn)程2:


      這樣莫名其妙的修復(fù)了bug,并且測(cè)試正常。但是我心里很疑惑為何這樣在CEditWnd里面調(diào)用SetNextTabControl可以切換焦點(diǎn)但是在CpaintManager的PreMessageHandler里面調(diào)用SetNextTabControl函數(shù)卻失效。而且這也沒(méi)法解釋為何這個(gè)bug只存在于彈出窗體而不是主窗體中,后來(lái)才意想到問(wèn)題的緣由根本不在于CEditWnd和PreMessageHandler!


      接著分析進(jìn)程1以后,我1直調(diào)試SetNextTabControl函數(shù)和SetFocus函數(shù),下了很多條件斷點(diǎn)和數(shù)據(jù)斷點(diǎn),試圖找到在CPaintManager的PreMessageHandler里面調(diào)用SetNextTabControl函數(shù)失效的緣由。最后發(fā)現(xiàn)履行PreMessageHandler的CpaintManager類(lèi)根本不是彈出窗體的CPaintManager,而是主窗體的CPaintManager!主窗體的CPaintManager調(diào)用了SetNextTabControl,他是給主窗體的控件切換了焦點(diǎn)!而彈出的子窗體的CPaintManager根本沒(méi)有履行PreMessageHandler函數(shù),所以他的SetNextTabControl失效了,而我莫名其妙的在CEditWnd里面調(diào)用了SetNextTabControl歪打正著的調(diào)用了彈出窗體的SetNextTabControl。這就解析了分析進(jìn)程1中為何看上去修復(fù)了bug。


      那末現(xiàn)在就要分析1下為何明明在彈出窗體中按了Tab鍵,最后調(diào)用的卻是主窗體的PreMessageHandler函數(shù)。


      這要從duilib的最底層消息處理函數(shù)說(shuō)起,他是所以duilib程序消息的出發(fā)點(diǎn)。duilib的最底層消息處理函數(shù)有兩個(gè),1個(gè)是CWindowWnd類(lèi)的ShowModal函數(shù),1個(gè)是CPaintManager類(lèi)的MessageLoop函數(shù),這兩個(gè)函數(shù)有1個(gè)共同點(diǎn),共同的代碼以下:


while( ::IsWindow(m_hWnd) && ::GetMessage(&msg, NULL, 0, 0) ) {         if( msg.message == WM_CLOSE && msg.hwnd == m_hWnd ) {             nRet = msg.wParam;             ::EnableWindow(hWndParent, TRUE);             ::SetFocus(hWndParent);         }         if( !CPaintManagerUI::TranslateMessage(&msg) ) {             ::TranslateMessage(&msg);             ::DispatchMessage(&msg);         }         if( msg.message == WM_QUIT ) break;     }

      大家都知道win32程序的消息需要先調(diào)用GetMessage,然后調(diào)用win32的TranslateMessage和DispatchMessage函數(shù)來(lái)分派消息。而duililb在win32的TranslateMessage之前先調(diào)用了CPaintManager中的1個(gè)名為T(mén)ranslateMessage的靜態(tài)函數(shù)來(lái)過(guò)濾消息。而這個(gè)TranslateMessage才是bug的出處!他的代碼以下:


<pre name="code" class="cpp">bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg) { // Pretranslate Message takes care of system-wide messages, such as // tabbing and shortcut key-combos. We'll look for all messages for // each window and any child control attached. UINT uStyle = GetWindowStyle(pMsg->hwnd); UINT uChildRes = uStyle & WS_CHILD; LRESULT lRes = 0; if (uChildRes != 0) { HWND hWndParent = ::GetParent(pMsg->hwnd); for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]); HWND hTempParent = hWndParent; while(hTempParent) { if(pMsg->hwnd == pT->GetPaintWindow() || hTempParent == pT->GetPaintWindow()) { if (pT->TranslateAccelerator(pMsg)) return true; if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return true; return false; } hTempParent = GetParent(hTempParent); } } } else { for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { int size = m_aPreMessages.GetSize(); CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]); if(pMsg->hwnd == pT->GetPaintWindow()) { if (pT->TranslateAccelerator(pMsg)) return true; if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return true; return false; } } } return false; }



       我來(lái)分析1下致使bug的緣由。首先說(shuō)1下當(dāng)窗體中沒(méi)有CEditUI或CWebBrowserUI控件的情況。函數(shù)進(jìn)入后調(diào)用者兩行代碼判斷發(fā)送消息的窗體是否是子窗體

UINT uStyle = GetWindowStyle(pMsg->hwnd); UINT uChildRes = uStyle & WS_CHILD;

      如果沒(méi)有CEditUI或CWebBrowserUI控件,通常情況下就不會(huì)有子窗體,那末TranslateMessage往下履行后if (uChildRes != 0)判斷就不會(huì)成功,也就是會(huì)調(diào)用else里面的代碼。在else里面,會(huì)遍歷m_aPreMessages數(shù)組中的元素(m_aPreMessages是全局變量,里面保存了所有窗體的CPaintManager對(duì)象的指針),然后調(diào)用每一個(gè)元素的PreMessageHandler函數(shù),直到消息被處理。


      而如果包括CEditUI或CWebBrowserUI控件,那末他們內(nèi)部就會(huì)創(chuàng)建win32原生的控件(也就是子窗體),那末if (uChildRes != 0)判斷就會(huì)成功,任然是順次遍歷m_aPreMessages數(shù)組的元素,但是代碼有些不同


CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]); HWND hTempParent = hWndParent; while(hTempParent) { if(pMsg->hwnd == pT->GetPaintWindow() || hTempParent == pT->GetPaintWindow()) { if (pT->TranslateAccelerator(pMsg)) return true; if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return true; return false; } hTempParent = GetParent(hTempParent); }


         其中的hTempParent句柄會(huì)在while循環(huán)中被GetParent函數(shù)修改。問(wèn)題就在這里了!當(dāng)遍歷到m_aPreMessages的的元素,也就是主窗體的CPaintManager時(shí)


if(pMsg->hwnd == pT->GetPaintWindow() || hTempParent == pT->GetPaintWindow())
           

         這句代碼的hTempParent == pT->GetPaintWindow()會(huì)被判斷為成功,由于win32原生控件句柄屢次GetParent后就會(huì)得到主窗體的句柄,這時(shí)候hTempParent的值就和m_aPreMessages的第1個(gè)元素,也就是pT->GetPaintWindow()的結(jié)構(gòu)相同。

     

         判斷成功后,會(huì)調(diào)用pT->PreMessageHandler,履行主窗體的PreMessageHandler函數(shù),然后通過(guò)PreMessageHandler的代碼可以知道,主窗體設(shè)置了自己的Tab焦點(diǎn)后,履行了return true。而PreMessageHandler返回true,在這個(gè)TranslateMessage里面也就返回了true,這時(shí)候TranslateMessage就結(jié)束了。明顯看到,這類(lèi)情況下,彈出窗體的CPaintManager根本沒(méi)法履行PreMessageHandler函數(shù),這就解析了為何子窗體的CEditUI和CWebBrowserUI沒(méi)法切換焦點(diǎn)而主窗體可以。


          這下子找到了本源,分析進(jìn)程1的修復(fù)代碼就是沒(méi)必要的,這里這樣修改代碼后,bug就修復(fù)了。(注意,終究的bug修復(fù)代碼只需要修改這1個(gè)函數(shù)就好了,之前分析進(jìn)程1的不需要修改了?。?/span>


bool CPaintManagerUI::TranslateMessage(const LPMSG pMsg) { // Pretranslate Message takes care of system-wide messages, such as // tabbing and shortcut key-combos. We'll look for all messages for // each window and any child control attached. UINT uStyle = GetWindowStyle(pMsg->hwnd); UINT uChildRes = uStyle & WS_CHILD; LRESULT lRes = 0; if (uChildRes != 0) { HWND hWndParent = ::GetParent(pMsg->hwnd); for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]); HWND hTempParent = hWndParent; while(hTempParent) { if(pMsg->hwnd == pT->GetPaintWindow() || hTempParent == pT->GetPaintWindow()) { if (pT->TranslateAccelerator(pMsg)) return true; pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes); // if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) // return true; // // return false; //code by redrain } hTempParent = GetParent(hTempParent); } } } else { for( int i = 0; i < m_aPreMessages.GetSize(); i++ ) { int size = m_aPreMessages.GetSize(); CPaintManagerUI* pT = static_cast<CPaintManagerUI*>(m_aPreMessages[i]); if(pMsg->hwnd == pT->GetPaintWindow()) { if (pT->TranslateAccelerator(pMsg)) return true; if( pT->PreMessageHandler(pMsg->message, pMsg->wParam, pMsg->lParam, lRes) ) return true; return false; } } } return false; }

        修復(fù)代碼很簡(jiǎn)單,不讓他return,而是繼續(xù)把消息傳遞下去。附效果圖:



        幾經(jīng)波折,前后我分析和調(diào)試了4個(gè)多小時(shí)duilib,終究只要修改3行代碼,bug就修復(fù)了。


總結(jié):

       實(shí)際的修復(fù)進(jìn)程其實(shí)不是文章描寫(xiě)的這么順利,期間修改過(guò)量次CEditUI的控件代碼也實(shí)現(xiàn)了焦點(diǎn)切換,還該多其他地方的很多代碼,我就不在文章中描寫(xiě)了。而在后續(xù)的調(diào)試進(jìn)程中才發(fā)現(xiàn)了原來(lái)問(wèn)題的根本在于CPaintManager中的TranslateMessage消息處理。幾次周轉(zhuǎn)總算修復(fù)了bug。但是我還沒(méi)有對(duì)這個(gè)修復(fù)的代碼進(jìn)行完全的測(cè)試,不知道他會(huì)不會(huì)引發(fā)甚么新的問(wèn)題。所以如果有打算修復(fù)這個(gè)bug的朋友建議你多做1些測(cè)試,如果發(fā)現(xiàn)有甚么問(wèn)題,請(qǐng)?jiān)诓┛椭辛粞曰騋Q上告知我1下,謝謝~~


Redrain   2014.11.28


QQ:491646717

      

生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 日韩高清成人 | 一区二区在线视频 | 爱爱小视频网站 | 亚洲男人在线 | 国产精选h网站 | 欧美片子| 久久精品国产一区二区电影 | 成人免费视频国产 | 国产精品久久久久久久午夜 | 精品欧美乱码久久久久久1区2区 | 日韩国产一区二区 | 国产精品久久综合 | 欧美三区视频 | 一区二区日本视频 | 精品国产乱码久久久久久牛牛 | 国产a区 | 国产成人精品综合 | 色先锋影院 | 国产乱码精品一区二区三 | 99在线免费观看视频 | 中文字幕亚洲一区二区三区 | v天堂福利视频在线观看 | 热久热久 | 91精品久久久久久久99蜜桃 | 色玖玖 | 精品久久久久久久久久久久 | 精产国产伦理一二三区 | 九一在线观看 | 日韩福利在线 | www.日| 国产激情在线观看 | 免费视频在线观看网站 | 国产成人精品一区二 | 亚洲午夜av久久乱码 | 成人av在线网址 | 久久久麻豆视频 | 日批av | 麻豆最新网址 | 91麻豆精品| 极品视频在线 | 精品成人国产 |