關(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、同第二種
oldProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);SetWindowLong(hWnd, GWL_WNDPROC, (LONG) ProcButton);第四種:不用 Hook
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)格。
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è)換膚的完善的解決方案。
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關(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攔截。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);}
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)世事了。
聯(lián)系客服