国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
關(guān)于如何換膚、子類化的解決方案
 

關(guān)于如何換膚、子類化的解決方案

作者:peterbing@sohu.com

  對(duì)于應(yīng)用程序的換膚及子類化。下面是我嘗試過(guò)一些方法,以在CAboutDlg中子類化其中的Button為例:

第一種:直接用現(xiàn)成的類

1、自己寫(xiě)一個(gè)類class CButtonXP : public CButton{/*...*/}

用MessageMap處理感興趣的消息。

2、用CButtonXP代替CButton來(lái)聲明變量m_btn;

3、在void CAboutDlg:DoDataExchange(CDataExchange* pDX)中加上一句:

DDX_Control(pDX, IDB_BUTTON1, m_edit);

或者在 InitDialog() 中加上

m_btn.SubclassDlgItem(IDB_BUTTON1, this);

這兩種效果差不多的。

第二種:在 Hook 中使用現(xiàn)成的類

1、自己寫(xiě)一個(gè)類 class CButtonXP : public CButton{/*...*/}

用 MessageMap 處理感興趣的消息。

2、使用 SetWindowsHookEx 安裝一個(gè)鉤子:

g_hWndProcHook = ::SetWindowsHookEx(WH_CALLWNDPROC,WndProcHook,NULL,::GetCurrentThreadId());

3、在 WndProcHook 中處理窗口創(chuàng)建和銷毀的消息:

LRESULT CALLBACK WndProcHook(int code, WPARAM wParam, LPARAM lParam){if (code == HC_ACTION){switch (((CWPSTRUCT*) lParam)->message){case WM_CREATE:BeginSubclassing(((CWPSTRUCT*) lParam)->hwnd);break;case WM_NCDESTROY:// TODO: clear subclass info.EndSubclassing(((CWPSTRUCT*) lParam)->hwnd);break;default:break;}}return CallNextHookEx(g_hWndProcHook, code, wParam, lParam);}      
4、在 BeginSubclassing 中用 GetClassName 得到類名,例如 "Button",然后用 CButtonXP 類進(jìn)行子類化。
CButtonXP pButton = new CButtonXP;VERIFY(pButton ->SubclassWindow(hWnd));

第三種 在Hook中使用窗口過(guò)程

1、自己寫(xiě)一個(gè)按鈕的窗口過(guò)程

WNDPROC oldProc;LRESULT CALLBACK ProcButton(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){ASSERT(oldProc != 0);if (oldProc == 0) return TRUE;switch (uMsg){case WM_ERASEBKGND:break;//......default:break;}return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);}      
2、同第二種
3、同第二種

4、在 BeginSubclassing 中得到類名后,用 SetWindowLong 的方式子類化:
oldProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);SetWindowLong(hWnd, GWL_WNDPROC, (LONG) ProcButton);      
第四種:不用 Hook

在一個(gè)對(duì)話框的 OnInitDialog 中枚舉它的所有子窗體,例如用下面兩句來(lái)實(shí)現(xiàn):
hWnd=GetWindow(hDlg,GW_CHILD);hWnd=GetWindow(hWnd,GW_HWNDNEXT);

對(duì)每個(gè)子窗體進(jìn)行子類化處理,處理過(guò)程同第二種與第三種。

第五種:如果是在XP下運(yùn)行,可以使用manifest,也就是如下的一個(gè)XML文件
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><assemblyIdentity name="Microsoft.Windows.XXXX"processorArchitecture="x86"version="5.1.0.0"type="win32"/><description>Windows Shell</description><dependency><dependentAssembly><assemblyIdentity type="win32"name="Microsoft.Windows.Common-Controls"version="6.0.0.0"processorArchitecture="x86"publicKeyToken="6595b64144ccf1df"language="*"/></dependentAssembly></dependency></assembly>       
  把它存為應(yīng)用程序名 .manifest,放到和應(yīng)用程序?qū)?yīng)的目錄下,或者把它作為資源類型為24的資源編譯進(jìn)應(yīng)用程序中。這樣程序在XP下就自動(dòng)擁有了XP的風(fēng)格。

第六種:使用第三方的庫(kù)Skin++(www.uipower.com)實(shí)現(xiàn)換膚

第七種:用第三方應(yīng)用程序給整個(gè)windows換膚(windowblinds)

  以上七種方式各有優(yōu)缺點(diǎn)。我在使用過(guò)程中也遇到不少問(wèn)題,現(xiàn)在一一道來(lái),希望和大家共同解決問(wèn)題。先排除幾種不準(zhǔn)備深入探討的方式:

第五種,manifest 方式最快速和簡(jiǎn)潔,但是功能有限,存在嚴(yán)重的平臺(tái)限制,不過(guò)好處在于應(yīng)用程序可以和windows共一種風(fēng)格。
第六種,使用第三方的庫(kù) Skin++(www.uipower.com) 實(shí)現(xiàn)換膚方式使用起來(lái)很簡(jiǎn)單,定制性也不錯(cuò),可供選擇的皮膚種類非常的多,支持的語(yǔ)言非常廣泛,可以稱得上是換膚功能的終結(jié)者,對(duì)于共享軟件開(kāi)發(fā)者和注重界面的企業(yè)來(lái)說(shuō)是個(gè)不錯(cuò)的解決方案,他的換膚理念很新,有些地方做得很獨(dú)特,比如可以對(duì) BCG 換膚等,有些技術(shù)點(diǎn),很多同類產(chǎn)品都沒(méi)有做到,比如 ComboBox 的滾動(dòng)條,系統(tǒng)對(duì)話框(open or close Dialog)的菜單等等。
第七種,屬于自?shī)市再|(zhì)的,也就不多說(shuō)了。
第一種,直接使用現(xiàn)成的類,屬于很常見(jiàn)的一種用法,一般來(lái)說(shuō)使用上不會(huì)出什么問(wèn)題,缺點(diǎn)就不說(shuō)了,如果這種方式讓我滿意,我就不必發(fā)這篇帖子了。

下面看看第二三四種:

第二種是用 HOOK+ 窗口類,實(shí)現(xiàn)起來(lái)比較方便,和做一個(gè)自繪控件的工作量其實(shí)是一樣的。
第三種是用HOOK+窗口過(guò)程,實(shí)現(xiàn)起來(lái)比較麻煩,需要自己處理一堆switch case, 自己轉(zhuǎn)換消息參數(shù),自己找地方維護(hù)一堆狀態(tài)變量,工作量很大。
第四種不用 HOOK 的方式,有個(gè)缺點(diǎn):對(duì)被換膚的程序的源代碼的修改比較多。當(dāng)然,直接到進(jìn)程中去找窗口句柄,然后子類化那么就不用源代碼了,不過(guò)這樣的話還不如用HOOK呢。
  實(shí)際上,HOOK機(jī)制和枚舉窗體雖然過(guò)程不同,不過(guò)最終目的是一樣的,都是為了子類化窗口。所以在此不去探討孰優(yōu)孰劣了?,F(xiàn)在切入正題,談?wù)勗谧宇惢^(guò)程中遇到的問(wèn)題:
  一個(gè)是重復(fù) subclass 的問(wèn)題,上面提到,子類化的兩種方式:用窗口類或者用窗口過(guò)程。使用窗口類是從CWnd派生一個(gè)類,調(diào)用CWnd 的 protected 函數(shù) SubclassWindow。可是如果正常使用一個(gè)窗口類(聲明成員變量,加入DDX_Control),實(shí)際上在 DDX_Control 中也是是用了 SubclassWindow 的。假如為一個(gè)控件聲明變量,而在 Hook 中又進(jìn)行了子類化,結(jié)果會(huì)怎么樣呢?答案是:程序崩潰或彈出消息框"不支持的操作"。因?yàn)?SubclassWindow 函數(shù)調(diào)用前是要先 Attach 到一個(gè)HWND上去的。重復(fù)的 Attach 看來(lái)是不允許。要避免程序崩潰也有辦法:

1、只為控件聲明一個(gè)指針變量,動(dòng)態(tài)的去獲取CWnd類的實(shí)例,但是這樣就達(dá)不到換膚的目的了。
2、還有一種方法,經(jīng)過(guò)我試驗(yàn),如果兩個(gè)SubclassWindow的調(diào)用位于不同的模塊,例如一個(gè)位于exe,一個(gè)位于dll(我是通過(guò)exe中調(diào)用dll中的函數(shù)顯示該dll中的對(duì)話框來(lái)測(cè)試的),那么就不會(huì)出現(xiàn)問(wèn)題。在還沒(méi)有找到更好的方法之前,這也姑且算是一種解決方法吧。
  但是如果使用窗口過(guò)程來(lái)子類化,就不存在重復(fù)subclass的問(wèn)題了,只要小心處理,子類化無(wú)數(shù)次都沒(méi)問(wèn)題,但是對(duì)于復(fù)雜的自繪事件,在一個(gè)窗口過(guò)程中來(lái)寫(xiě)switch語(yǔ)句,好像很麻煩。
  我嘗試過(guò)自己寫(xiě)一個(gè)新的SubclassWindow函數(shù)來(lái)嘗試借用CWnd的窗口過(guò)程,這樣就可以按照MFC的方式來(lái)寫(xiě)消息響應(yīng)函數(shù)了。只可惜,最終還是無(wú)功而返,因?yàn)镾ubclassWindow不是虛函數(shù),而CWnd的窗口過(guò)程是作為一個(gè)protected成員存在的。所以沒(méi)法在外部借用MFC的消息機(jī)制。所以,自己寫(xiě)代碼處理 wParam 和 lParam 看來(lái)在所難免。
  零一個(gè)是子類化系統(tǒng)對(duì)話框的問(wèn)題,系統(tǒng)的對(duì)話框和自己的對(duì)話框表現(xiàn)的總不一樣。目前我還沒(méi)有對(duì)所有的系統(tǒng)對(duì)話框進(jìn)行測(cè)試。在 MessageBox 彈出的對(duì)話框中遇到的問(wèn)題可以見(jiàn)我這一片帖子:
http://community.csdn.net/Expert/To....asp?id=3103399

  在文件對(duì)話框中我遇到一個(gè)問(wèn)題,子類化過(guò)的 CStatic 的背景好像沒(méi)有重繪一樣,照理說(shuō)應(yīng)該由CStatic的父窗體負(fù)責(zé)背景的。
  我在我的 CStaticNew 類中只重載了 OnPaint,里面只處理文字和圖標(biāo)的繪制,背景的繪制留給父窗體完成。這樣的處理在 MessageBox 和自己的 AboutDlg 中都沒(méi)有問(wèn)題,Static 控件的背景就是父窗口的背景,可是在 CFileDlg 中背景就沒(méi)有重繪了:

void CStaticNew::OnPaint(){CPaintDC dc(this); // device context for painting// TODO: Add your message handler code hereCRect rt;GetWindowRect(rt);// 繪制背景dc.SetBkMode(TRANSPARENT);// 繪制文字CFont *pfont, * pOldFont;pfont = GetFont();if (pfont)pOldFont = dc.SelectObject(pfont);CString szTitle;GetWindowText(szTitle);dc.DrawText(szTitle, CRect(0, 0, rt.Width(), rt.Height()), DT_LEFT | DT_WORDBREAK );if (pfont)dc.SelectObject(pOldFont);// 繪制圖標(biāo)if ((GetStyle() & SS_ICON) != 0){dc.DrawIcon(0, 0, GetIcon());}// Do not call CStatic::OnPaint() for painting messages}      
  類名的識(shí)別問(wèn)題,到現(xiàn)在為止,我所使用的子類化方法都是基于GetClassName這個(gè)函數(shù)獲得窗口類名,再根據(jù)用spy++所得到的知識(shí),如"#32770"表示對(duì)話框,"ToolbarWindow32"是工具欄,等等。但是窗口類名是可以在創(chuàng)建時(shí)任意指定的呀,而像CMainFrame的類名根本就不能夠確定,例如記事本主窗體的類名是"Notepad",寫(xiě)字板主窗體的類名是"WordPadClass"。這樣的話,子類化如何去進(jìn)行呢。真想知道windows是怎么做的,skinmagic又是怎么做的。目前主要就是這三個(gè)問(wèn)題了。希望大家能展開(kāi)討論,給出一個(gè)換膚的完善的解決方案。
  我寫(xiě)了一個(gè)簡(jiǎn)化的CWnd類來(lái)解決重復(fù)子類化問(wèn)題和簡(jiǎn)化窗口過(guò)程,不過(guò)它不支持對(duì)自己的重復(fù)子類化(即只能用于沒(méi)有被子類化的或者被CWnd子類化的HWND)。
  因?yàn)椴幌肱煤蚆essageMap那樣復(fù)雜,所以功能也有限:須手工轉(zhuǎn)化WPARAM和LPARAM、消息處理無(wú)法繼承、不支持多線程。使用很簡(jiǎn)單:
CWndNew* pWnd = new CWndNew;pWnd->SubclassWindow(hWnd);

用完了,記得釋放處理:

pWnd->UnsubclassWindow();delete pWnd;

如果要進(jìn)行功能擴(kuò)充(繼承),就改寫(xiě)那幾個(gè)虛函數(shù):

class CWndNew{public:CWndNew();virtual ~CWndNew();bool SubclassWindow(HWND hWnd);void UnsubclassWindow();protected: // virtualvirtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);virtual void PresubclassWindow(){};virtual void PostunsubclassWindow(){};protected:LRESULT PrevWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);HWND m_hWnd;private:WNDPROC m_oldProc;static map m_map;static LRESULT CALLBACK StaticWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);};//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////map CWndNew::m_map;CWndNew::CWndNew(){m_hWnd = NULL;}CWndNew::~CWndNew(){ASSERT(m_hWnd == NULL);}bool CWndNew::SubclassWindow(HWND hWnd){m_map[hWnd] = this;ASSERT(m_hWnd == NULL);m_hWnd = hWnd;//允許派生類在子類化之前做一些初始化.PresubclassWindow();m_oldProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);ASSERT(m_oldProc != 0);SetWindowLong(hWnd, GWL_WNDPROC, (LONG) StaticWindowProc);return true;}void CWndNew::UnsubclassWindow(){SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_oldProc);PostunsubclassWindow();m_map.erase(m_hWnd);m_hWnd = NULL;}LRESULT CALLBACK CWndNew::StaticWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){CWndNew* pWnd = m_map[hWnd];ASSERT(pWnd != NULL);return pWnd->WindowProc(uMsg, wParam, lParam);}LRESULT CWndNew::PrevWindowProc(UINT uMsg,WPARAM wParam,LPARAM lParam){return CallWindowProc(m_oldProc, m_hWnd, uMsg, wParam, lParam);}LRESULT CWndNew::WindowProc(UINT uMsg,WPARAM wParam,LPARAM lParam){return PrevWindowProc(uMsg, wParam, lParam);}      
  關(guān)于子類化及其撤銷的順序問(wèn)題,當(dāng)用自己的類或者過(guò)程子類化窗口時(shí),需要處理好與MFC類子類化的順序沖突。假設(shè)我們自己的類叫CWndNew,那么不管CWnd和CWndNew誰(shuí)先子類化一個(gè)窗口,最終兩者協(xié)同工作的結(jié)果應(yīng)該是該窗口的窗口過(guò)程還原到未子類化之前的狀態(tài)。首先,不要在HOOK過(guò)程中處理WM_NCDESTROY消息。理由:如果CWndNew比CWnd先子類化,由于HOOK的原因,你仍然會(huì)先處理WM_NCDESTROY,這時(shí)候如果你撤銷子類化,那么CWnd類就得不到機(jī)會(huì)清理。而如果你不撤銷子類化,CWnd沒(méi)有能力把被子類化的窗口還原到最初狀態(tài)。在HOOK過(guò)程中,不能通過(guò)調(diào)用SendMessage函數(shù)讓CWnd先行處理,然后你自己再處理,因?yàn)镾endMessage后,消息又會(huì)被HOOK攔截。
  由于上述原因,在CWndNew的消息過(guò)程中處理WM_NCDESTROY是很不錯(cuò)的選擇,MFC也是這樣做的。參照如下的代碼進(jìn)行解釋:
case WM_NCDESTROY:{LRESULT lret;WNDPROC wndproc;wndproc = (WNDPROC)GetWindowLong(m_hWnd, GWL_WNDPROC);if (wndproc == CWndNew::StaticWindowProc){HWND hWnd = m_hWnd;UnsubclassWindow();lret = CallWindowProc(m_oldProc,hWnd,uMsg,wParam,lParam);}else{lret = CallWindowProc(m_oldProc,m_hWnd,uMsg,wParam,lParam);if(wndproc == (WNDPROC)GetWindowLong(m_hWnd, GWL_WNDPROC))UnsubclassWindow();}delete this;return lret;}      
  首先判斷該窗口的WNDPROC是否發(fā)生過(guò)變動(dòng),如果沒(méi)有的話是最好的,趕緊撤銷子類化,再把消息傳遞給之前窗口過(guò)程,然后功成身退,不問(wèn)世事了。
  如果發(fā)生過(guò)變動(dòng),那么也就是說(shuō)有別的類在CWndNew子類化以后又進(jìn)行了子類化,而現(xiàn)在又把WM_NCDESTROY傳給了CWndNew。這好辦,如法炮制,把消息繼續(xù)往前傳,如果WNDPROC又發(fā)生了改變,說(shuō)明之前的某個(gè)窗口過(guò)程已經(jīng)作了處理,就不需要再進(jìn)行撤銷子類化的操作了。這點(diǎn)MFC的CWnd類也是這樣做的。
  另外還有一個(gè)問(wèn)題不解,就是Edit,ListBox,ListCtrl等等控件的內(nèi)嵌的滾動(dòng)條是怎么換膚的?網(wǎng)上一般介紹的方法是隱藏原來(lái)的,然后換上自己重新實(shí)現(xiàn)的。這種在Spy++中一看就能現(xiàn)出原形,可是Skin++ 換膚后的滾動(dòng)條就不知道是怎么實(shí)現(xiàn)的了?我看過(guò)coolsb這個(gè)文章,他能實(shí)現(xiàn)給滾動(dòng)條換膚的功能,但是對(duì)Combobox支持不好。

(#)
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
vc子類化和反子類化
子類化和超類化區(qū)別(轉(zhuǎn)自--眼見(jiàn)為實(shí)(2):介紹Windows的窗口、消息、子類化和超類化...
win32對(duì)話框
阿里旺旺分析系列一:實(shí)時(shí)獲取阿里旺旺聊天消息,實(shí)現(xiàn)旺旺客服機(jī)器人
MFC編程中的窗口子類化淺析
WM_COMMAND & WM_SYSCOMMAND
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服