實現令人滿意的風格統一的軟件界面確實很難, 象網友提到的MessageBox、FileDialog、FontDialog、目錄選擇對話框等MFC內部甚至系統DLL內的對話框,要想讓它變臉可不容易。有人說HOOK技術可以,HOOK技術確實可以,HOOK可以說是無孔不入,但HOOK的使用效率卻是令人難以滿意的,從目前大多數采HOOK技術的換膚軟件使用情況來看,完全可以證明這一點。今天我們將討論另外一技術來實現FileDialog的變臉,這種技術叫替換窗口過程法(注:本人杜撰)。
窗口過程函數是大多窗口都有的,它處理窗口中(包括子窗口)的每一個事件,替換窗口過程法與HOOK技術比起來孰強誰弱是很顯然的,因為同一個HOOK只能攔截一個事件,處理起來相當費時,HOOK也有它的優勢,但不是本文要討論的。這裡先讓大家眼見為實,請看下圖:
一、原理說明:
替換對話框的窗口過程要用到這個函數SetWindowLong()其原型如下:
LONG SetWindowLong(HWND hWnd, int nIndex , long dwLongNewProc)
hWnd為指定窗口的句柄,
nIndex 為 GWL_WNDPROC時才可設定新的窗口過程
dwLongNewProc 為指定新的窗口過程函數地址
反回值為一個long的數值,此值為舊的窗口過程函數地址。
如果要替換某個窗口的窗口過程函數,首先要想法弄到它的窗口的句柄,當然還得有窗口過程。
二、CFileDialog文件對話框界面設計
a. 從CFileDialog派生類CMyFileDialog;
b. 添加Protected 型虛成員函數:OnInitDone();
此函是實際上是CFileDialog一個虛函數,它在文件對話框創後建後被調用,它給我們留下了一個入口,因些重載現實替換其窗口過程函數;
c. 添加成員函數MyWindowProcNew()
static LRESULT CALLBACK WindowProcNew(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
在這函數實現的時候你可大顯身手,處理得好不好決定你是否能變臉成功。 d. 實現代碼及說明如下:
WNDPROC m_MyWndProc; //定義全局變量保存舊的窗口過程函數地址
void CMyFileDialog::OnInitDone()
{
CWnd* pDialog = GetParent();
m_MyWndProc=(WNDPROC)SetWindowLong(pDialog->m_hWnd,GWL_WNDPROC,(long)MyWindowProcNew);
}
LRESULT CALLBACK CMyFileDialog::MyWindowProcNew(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
switch (message)
{
case WM_NOTIFY:
case WM_MOUSEMOVE:
case WM_MOVE:
break;
case WM_NCACTIVATE:
break;
case WM_NCPAINT:
break;
case WM_NCMOUSEMOVE:
break;
case WM_PAINT:
break;
case WM_COMMAND:
break;
case WM_SIZE:
break;
case WM_XX:
default:
break;
}
//這時要恢復調用舊的窗口函數,當然必時可要忽略它
return CallWindowProc(m_MyWndProc, hwnd, message, wParam, lParam);
}
這個函數CallWindowProc()用來恢調用復舊窗口過程函數,當然這並不意味取消了新的窗口過程。你也可跳過它不去執行,那為什要恢復舊的窗口過程函數?因為舊的窗口過程函數它處理了太多的事件,而新窗口過程函數只處理我們關心的事件,如果你樂意的話你完全可不要調用舊的窗口過程函數,那可是要付出大的代價的。具體細節請大家去實現,可參“讓你的軟件界面更漂亮(一)”。
三、CMyFileDialog的用法
我把CMyFileDialog寫成一個DLL,有Debug和Release兩個版,請用時分別選擇 a.先把.lib .dll .h 文件持拷到工程目錄。在要使用處加如代碼:
#include "MyFileDialg.h"
#pragam comment(lib,"user.lib")
b.顯示文件對話框:
CMyFileDialog MyFileDlg(TRUE, 1, _T("對話框標題"));
MyFileDlg.DoModal();
CString FileName = MyFileDlg.GetPathName()
FileName是反回的結果
c. CMyFileDialog有一個超值的功能,不知有沒有從上圖看出來?它能夠用來代替 SHBrowseForFoler 作為目錄選擇對話框。這也是我寫CMyFileDialog的初終。構造函數第一個參數和CfileDialg 一樣,第二個參數為TRUE時可作為目錄選擇 對話框, 此時第一個參數可BOOL的任意值建議設為TRUE,第三個參數為對話框標題,如為NULL則顯示默認標題,其它參數和CFileDialog一樣。