介面庫技術概述

來源:互聯網
上載者:User

在做“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 <class TBase, class TWinTraits>
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中的一個例子,用於瞭解各種控制項的訊息。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.