界面庫技術概述
function
StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:'):(d.getSelection?d.getSelection():');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}
界面庫技術概述
在做“HOOK文件打開/保存對話框”的過程中,我首先研究了界面庫的相關知識。界
面庫一般都是由C/C++這種中低級語言編碼,這是因爲在Windows下的界面庫實現技術大都以直接操作控制Windows的消息和調用Windows
的API爲主,這就是這種中低級語言的優勢了。無論何種界面庫,最爲根本的原理就是獲得或者截獲窗口的某些消息,按照自己的需要處理這些消息,畫出自己需
要的界面。
按照Windows下的界面庫的使用方法來分類,可以分爲兩種:
1、 通過派生、繼承界面庫中的類來使用庫。這類界面庫現在是佔絕大多數。這類界面庫通常可以對同種類型的控件、窗口自己控制顯示風格。這種類型的界面庫典型的代表就是GuiToolkit、ProfUIS。
2、 通過Link頭文件,使用DLL來使用的界面庫。這類界面庫一般都是商業化的界面庫。這類界面庫一般對於同種類型的控件、窗口都是顯示統一的風格。這種類型的界面庫的典型代表是Skin++、AppFace。
上面的分類,其實同時也代表着兩種界面庫實現技術,也就是獲取用於自繪窗口的消息的兩種來源:
1、 通過子類化、超類化改變窗口風格。其實就是調用Windows的API SetWindowLong或者通過類的派生和繼承來改變Windows窗口的默認的消息處理函數。
2、 使用HOOK技術改變Windows的默認消息處理。
一、SetWindowLong
SetWindowLong(HWND hWnd,//需要改變UI的窗口的窗口句柄
Int nIndex,//替換窗口的默認消息處理函數時爲GWL_WNDPROC
Long dwNewLong)//新的默認的窗口消息處理函數
調用這個API函數可以替換一個窗口的默認的消息處理函數,這樣就可以在新的窗口消息處理函數中截獲到目標窗口的相關消息,然後根據需要處理這些消息。這個API用於獲取當前窗口的消息,它不能獲取窗口中子窗口的消息。
這種類型的界面庫,一般是開源的或者是提供了頭文件和Lib文件的界面庫。
這個Windows API大家很可能很少直接調用,但是它的封裝――――SubclassWindow這個成員函數,我想大家都使用過。先讓我們看看各種類型的子類化過程中的SetWindowLong都藏在什麼地方,它們是如何工作的。
1、 MFC下的實現
在MFC
程序中,可以先在工程中添加一個用於子類化的窗口類,然後就可以通過ClassWizard這個工具來完成剩下的子類化的工作了。我們以一個自定義的
Button類CMyButton類來舉例。在打開ClassWizard爲我們的基於Dialog的工程中的一個Button資源添加一個對應的變量的
時候我們就可以看到可以直接定義了CMyButton類,如下圖:
我們爲ID爲IDC_BUTTON1的一個按鈕資源定義一個類型爲CMyButton的成員變量m_MyBtn。這時候ClassWizard就會在重載的虛函數DoDataExchange中爲我們添加上一條語句,如下:
void CMFCSampleDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMFCSampleDlg)
DDX_Control(pDX, IDC_BUTTON1, m_MyBtn);//這就是ClassWizard爲我們添加的
//}}AFX_DATA_MAP
}
讓我們來看看DDX_Control這個函數爲我們做了什麼,在MFC的源代碼DLGDATA.CPP文件中我們能找到這個函數的源代碼:
void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
{
if (rControl.m_hWnd == NULL) // not subclassed yet
{
……
…
//注意看看,噢,原來SubclassWindow在這裏
if (!rControl.SubclassWindow(hWndCtrl))
{
ASSERT(FALSE); // possibly trying to subclass twice?
AfxThrowNotSupportedException();
}
……
…
}
}
讓我們再看看MFC下的SubclassWindow這個成員函數的實現,在MFC的源代碼wincore.cpp中可以看到所有MFC下窗口類的基類CWnd中的SubclassWindow的實現:
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();
//注意看看,原來它也是使用SetWindowLong啊
WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,
(DWORD)AfxGetAfxWndProc());
……
…
return TRUE;
}
上面的源代碼跟蹤說明,ClassWizard爲我們自動完成的子類化工作中,其實也是調用的SetWindowLong這個Windows API。當然在MFC下你也可以自己調用SubclassWindow這個成員函數去手動的完成子類化。
2、 ATL/WTL下的實現
在ATL的源代碼中,文件ATLWIN.H文件中,我們可以找到所有ATL窗口類的基類CwindowImplBaseT中的SubClassWindow的實現:
template
BOOL CWindowImplBaseT< TBase, TWinTraits >::SubclassWindow(HWND hWnd)
{
ATLASSERT(m_hWnd == NULL);
ATLASSERT(::IsWindow(hWnd));
m_thunk.Init(GetWindowProc(), this);
WNDPROC pProc = (WNDPROC)&(m_thunk.thunk);
//注意看看,它也是調用SetWindowLong的!
WNDPROC pfnWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);
if(pfnWndProc == NULL)
return FALSE;
m_pfnSuperWindowProc = pfnWndProc;
m_hWnd = hWnd;
return TRUE;
}
在ATL/WTL沒有MFC下的那種自動子類化的機制,如果需要子類化一般都是直接調用SubclassWindow這個成員函數。
二、SetWindowsHookEx
SetWindowsHookEx( int idHook,//HOOK的類型
HOOKPROC lpfn,//HOOK的回調函數
HINSTANCE hMod,//應用程序的實例句柄
DWORD dwThreadId)//線程的ID
SetWindowsHookEx
這個API可以設置很多種類型的HOOK,它們分別在不同的時間截獲線程ID爲dwThreadId的線程的不同消息。在編寫界面庫的時候一般設置類型爲
WH_CALLWNDPROC的HOOK,用以在窗口處理消息之前獲得消息並進行處理。在HOOK的CALLBACK函數中可以獲得窗口句柄,進而獲得窗
口的類型和窗口的風格,這樣就可以知道需要處理的消息類型。一般都是從截獲WM_CREATE消息開始,然後處理WM_PAINT、WM_NCPAINT
等等和UI有關的消息以達到自繪窗口UI的目的。
使用HOOK技術的界面庫中當然也可以使用SetWindowLong技術,在C++寫
的界面庫
中一般是在獲得了窗口句柄以後,即使用SetWindowLong技術將窗口句柄關聯到一種特定的窗口類上。並且可以根據窗口的風格讓同一個類做出不同風
格的顯示效果,可惜的是現在市面上的界面庫都很少進行這種比較繁瑣的區分工作。
這種類型的界面庫一般都是以DLL文件格式出現,現在最爲
流行的是 將界面庫封裝爲一個COM
DLL,在應用程序中創建這個COM組件,獲得相關的接口,調用相關的接口函數來爲應用程序安裝HOOK。在做“Picasso風格的文件打開/保存對話
框”過程中,我們一直以Yahoo Messenger的Save對話框作爲參考,其實Yahoo
Messenger就是使用的這種類型的界面庫的。Yahoo
messenger的界面庫截獲了進程中所有線程的消息,並做了相關的處理,所以它可以截獲一些系統的窗口的創建消息和UI相關的消息,以達到改變
Windows系統窗口的顯示風格的目的。
最後介紹一下幾個比較好的開源的界面庫或者例子。
1、 ClassXP(
http://www.yonsm.net/read.php?26
)
一個個人的開源界面庫,C語言寫的,不完善,使用HOOK技術。Yahoo messenger的界面庫就類似於這個界面庫,只不過Yahoo messenger做的更完善而已。
2、 ProfUIS(
http://www.codeproject.com/docking/prod_profuis.asp
)
一個部分開源的界面庫,有完善功能的商業版。比較完善,MFC寫的,使用的是SetWindowLong技術。
3、 GuiToolkit(
http://www.codeproject.com/library/guitoolkit.asp
)
一個開源的界面庫,比較完善,MFC寫的,使用的是SetWindowLong技術。
4、 MSDN中的ControlSpy例子。
MSDN中的一個例子,用於瞭解各種控件的消息。
http://blog.51CTO.net/vcleaner/archive/2006/06/04/772724.aspx
Trackback: http://tb.blog.51CTO.net/TrackBack.aspx?PostId=1861785
界面庫技術概述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章
Linux基本操作命令
wbzjacky
2019-02-24 13:12:38
真實的模擬***綜合實驗
wbzjacky
2019-02-24 13:12:37
三層交換機的HSRP、vlan、端口聚合
wbzjacky
2019-02-24 13:12:37
C++ 頭文件
sychen0608
2020-12-04 16:21:22
C / C++ 動態記憶體宣告及釋放
sychen0608
2020-12-02 16:05:24
如果同事暗中傷害你,應該怎麼辦?
這個饅頭有餡
2019-02-24 13:59:08
職場中,抱怨越多的員工,越被領導瞧不起!
這個饅頭有餡
2019-02-24 13:59:08
老程序員被裁,應屆生卻能月薪 1.3 萬?這你能忍?
前端高達
2019-02-24 13:48:04
遇到到處蹭吃卻從不請客吃飯的主怎麼辦?
樑軍年
2019-02-24 13:26:35
高標準機房綜合配線安裝
wbzjacky
2019-02-24 13:12:38
IPsec ***實驗
wbzjacky
2019-02-24 13:12:37
CISCO路由AAA的Easy ***
wbzjacky
2019-02-24 13:12:37