久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

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

 9loong 2010-09-19
 

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

作者:[email protected]

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

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

1,、自己寫一個類class CButtonXP : public CButton{/*...*/}

用MessageMap處理感興趣的消息。

2,、用CButtonXP代替CButton來聲明變量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,、自己寫一個類 class CButtonXP : public CButton{/*...*/}

用 MessageMap 處理感興趣的消息,。

2、使用 SetWindowsHookEx 安裝一個鉤子:

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中使用窗口過程

1,、自己寫一個按鈕的窗口過程

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

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

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

第五種:如果是在XP下運行,可以使用manifest,,也就是如下的一個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下就自動擁有了XP的風(fēng)格,。

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

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

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

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

下面看看第二三四種:

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

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

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

void CStaticNew::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CRect 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
}      
  類名的識別問題,,到現(xiàn)在為止,,我所使用的子類化方法都是基于GetClassName這個函數(shù)獲得窗口類名,再根據(jù)用spy++所得到的知識,,如"#32770"表示對話框,,"ToolbarWindow32"是工具欄,等等,。但是窗口類名是可以在創(chuàng)建時任意指定的呀,,而像CMainFrame的類名根本就不能夠確定,例如記事本主窗體的類名是"Notepad",寫字板主窗體的類名是"WordPadClass",。這樣的話,,子類化如何去進(jìn)行呢。真想知道windows是怎么做的,,skinmagic又是怎么做的,。目前主要就是這三個問題了。希望大家能展開討論,,給出一個換膚的完善的解決方案,。
  我寫了一個簡化的CWnd類來解決重復(fù)子類化問題和簡化窗口過程,不過它不支持對自己的重復(fù)子類化(即只能用于沒有被子類化的或者被CWnd子類化的HWND),。
  因為不想弄得和MessageMap那樣復(fù)雜,,所以功能也有限:須手工轉(zhuǎn)化WPARAM和LPARAM、消息處理無法繼承,、不支持多線程,。使用很簡單:
CWndNew* pWnd = new CWndNew;
pWnd->SubclassWindow(hWnd);

用完了,記得釋放處理:

pWnd->UnsubclassWindow();
delete pWnd;

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

class CWndNew
{
public:
CWndNew();
virtual ~CWndNew();
bool SubclassWindow(HWND hWnd);
void UnsubclassWindow();
protected: // virtual
virtual 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)于子類化及其撤銷的順序問題,,當(dāng)用自己的類或者過程子類化窗口時,需要處理好與MFC類子類化的順序沖突,。假設(shè)我們自己的類叫CWndNew,那么不管CWnd和CWndNew誰先子類化一個窗口,最終兩者協(xié)同工作的結(jié)果應(yīng)該是該窗口的窗口過程還原到未子類化之前的狀態(tài),。首先,,不要在HOOK過程中處理WM_NCDESTROY消息。理由:如果CWndNew比CWnd先子類化,,由于HOOK的原因,,你仍然會先處理WM_NCDESTROY,,這時候如果你撤銷子類化,那么CWnd類就得不到機會清理,。而如果你不撤銷子類化,,CWnd沒有能力把被子類化的窗口還原到最初狀態(tài)。在HOOK過程中,,不能通過調(diào)用SendMessage函數(shù)讓CWnd先行處理,,然后你自己再處理,因為SendMessage后,,消息又會被HOOK攔截,。
  由于上述原因,在CWndNew的消息過程中處理WM_NCDESTROY是很不錯的選擇,,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ā)生過變動,如果沒有的話是最好的,,趕緊撤銷子類化,,再把消息傳遞給之前窗口過程,然后功成身退,,不問世事了,。
  如果發(fā)生過變動,那么也就是說有別的類在CWndNew子類化以后又進(jìn)行了子類化,,而現(xiàn)在又把WM_NCDESTROY傳給了CWndNew,。這好辦,如法炮制,,把消息繼續(xù)往前傳,,如果WNDPROC又發(fā)生了改變,說明之前的某個窗口過程已經(jīng)作了處理,,就不需要再進(jìn)行撤銷子類化的操作了,。這點MFC的CWnd類也是這樣做的。
  另外還有一個問題不解,,就是Edit,ListBox,ListCtrl等等控件的內(nèi)嵌的滾動條是怎么換膚的,?網(wǎng)上一般介紹的方法是隱藏原來的,然后換上自己重新實現(xiàn)的,。這種在Spy++中一看就能現(xiàn)出原形,,可是Skin++ 換膚后的滾動條就不知道是怎么實現(xiàn)的了?我看過coolsb這個文章,,他能實現(xiàn)給滾動條換膚的功能,,但是對Combobox支持不好。

(#)

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多