“窗口類的封裝,從全局窗口消息處理到窗口對象消息處理的映射方法:
對界面進行封裝,一般都是一個窗口一個類,比如實現(xiàn)一個最基本的窗口類CMyWnd,你一定會把窗口過程作為這個類的成員函數(shù),但是使用WINAPI創(chuàng)建窗口時必須注冊類WNDCLASS,里面有個成員數(shù)據(jù)lpfnWndProc需要WNDPROC的函數(shù)指針,一般想法就是把窗口類的消息處理函數(shù)指針傳過去,但是類成員函數(shù)除非是靜態(tài)的,否則無法轉(zhuǎn)換到WNDPROC,而全局的消息處理函數(shù)又無法得到窗口類對象的指針。這里有幾種解決辦法:
一種解決方法是用窗口列表,開一個結(jié)構(gòu)數(shù)組,窗口類對象創(chuàng)建窗口的時候把窗口HWND和this指針放入數(shù)組,全局消息處理函數(shù)遍歷數(shù)組,利用HWND找出this指針,然后定位到對象內(nèi)部的消息處理函數(shù)。這種方法查找對象的時間會隨著窗口個數(shù)的增多而增長。
另一種方法比較聰明一點,WNDCLASS里面有個成員數(shù)據(jù)cbWndExtra一般是不用的,利用這點,注冊類時給該成員數(shù)據(jù)賦值,這樣窗口創(chuàng)建時系統(tǒng)會根據(jù)該值開辟一塊內(nèi)存與窗口綁定,這時把創(chuàng)建的窗口類的指針放到該塊內(nèi)存,那么在靜態(tài)的窗口消息循環(huán)函數(shù)就能利用GetWindowLong(hWnd,GWL_USERDATA)取出該指針,return (CMyWnd*)->WindowProc(...),這樣就不用遍歷窗口了。但是這樣一來就有個致命弱點,對窗口不能調(diào)用SetWindowLong(hWnd,GWL_USERDATA,數(shù)據(jù)),否則就會導致程序崩潰。幸好這個函數(shù)(特定這幾個參數(shù))是調(diào)用幾率極低的,對于窗口,由于創(chuàng)建窗口都是調(diào)用窗口類的Create函數(shù),不用手工注冊WNDCLASS類,也就不會調(diào)用SetWindowLong函數(shù)。但是畢竟缺乏安全性,而且當一秒鐘內(nèi)處理的窗口消息很多時,這種查找速度也可能不夠快。
”
創(chuàng)建窗口時: SetWindowLong( m_hWnd, GWL_USERDATA, (LONG) this );
函數(shù)功能描述:用這個函數(shù)能夠獲得指定窗口的信息
函數(shù)原型:
LONG GetWindowLong( HWND hWnd,int nIndex )
參數(shù):
hWnd:指定窗口的句柄
nIndex:需要獲得的信息的類型
值 功能
nIndex取值如下:
GWL_EXSTYLE 得到擴展的窗口風格
GWL_STYLE 得到窗口風格
GWL_WNDPROC 得到窗口回調(diào)函數(shù)的地址,或者句柄。得到后必須使用CallWindowProc函數(shù)來調(diào)用
GWL_HINSTANCE 得到應用程序運行實例的句柄
GWL_HWNDPARENT 得到父窗口的句柄
GWL_ID 得到窗口的標識符
GWL_USERDATA 得到和窗口相關(guān)聯(lián)的32位的值(每一個窗口都有一個有意留給創(chuàng)建窗口的應用程序是用的32位
的值)
當hWnd標識一個對話框時可以使用下面的值
Value Action
DWL_DLGPROC 得到對話框回調(diào)函數(shù)的地址,或者句柄。得到后必須使用CallWindowProc函數(shù)來調(diào)用
DWL_MSGRESULT 得到對話框回調(diào)函數(shù)中消息處理過程的返回值
DWL_USER 得到額外的應用程序私有信息,如一些句柄和指針等
返回值:
成功時,返回一個請求的32位的值
失敗時,返回0,可以使用GetLastError來取得錯誤信息
示例:
long nStyle = ::GetWindowLong(hWnd, GWL_STYLE); // hWnd是一個編輯框的句柄
if(nStyle & ES_PASSWORD)
{
AfxMessageBox("這是一個密碼域");
}
下面是一個具體的應用:
星號密碼查看工具大家都用過吧?現(xiàn)在我們自己來寫一個超級簡單的。密碼框其實就是Windows的一個子窗口,顯示星號是因為密碼框設置了EM_SETPASSWORDCHAR屬性,只要我們把密碼框的EM_SETPASSWORDCHAR屬性去掉,那么密碼就會以明文顯示了。我們可以給程序發(fā)送消息去掉EM_SETPASSWORDCHAR屬性,通過安裝鼠標鉤子監(jiān)視鼠標動作,如果用戶單擊的是密碼框,那么就發(fā)送一個去除密碼屬性的消息。
本文使用的編程工具為VC6.0,具體實現(xiàn)步驟和代碼如下。
1)生成一個基于對話框的程序pass。打開passDlg.cpp,加入下面的全局變量和鼠標鉤子函數(shù)。
HHOOK g_hHook = NULL;//全局鉤子函數(shù)句柄
//鼠標鉤子函數(shù)
LRESULT CALLBACK HookProc( int code, WPARAM wParam,LPARAM lParam )
{
HWND hwnd;
POINT point;
GetCursorPos(&point);
//得到鼠標位置
hwnd=::WindowFromPoint(point);
//得到包含鼠標的窗口句柄
long nStyle=::GetWindowLong(hwnd,GWL_STYLE);
//得到窗口風格
EVENTMSG *event=(EVENTMSG *)lParam;
if(event->message==WM_LBUTTONDOWN)
//是否為鼠標左鍵
{
if(nStyle & ES_PASSWORD)
//是否為密碼框
{
::PostMessage(hwnd, EM_SETPASSWORDCHAR,0,0);
//去掉密碼屬性
}
}
return CallNextHookEx(g_hHook,code,wParam,lParam);
}
這里需要注意的是,程序中的::PostMessage(hwnd, EM_SETPASSWORDCHAR,0,0);只能是PostMessage,而不能用SendMessage代替。
2)添加“開始探測”按鈕及響應函數(shù)OnOK(),并在函數(shù)中安裝鉤子。
void CPassDlg::OnOK()
{
g_hHook=SetWindowsHookEx(WH_JOURNALRECORD,HookProc,GetModuleHandle(NULL),0);
//安裝鉤子
}
鉤子的第三部分使用GetModuleHandle(NULL)函數(shù),意為把自己作為保存鉤子的DLL。
3)添加“取消退出”按鈕及響應函數(shù)OnExit(),并在函數(shù)中卸載鉤子。
void CPassDlg::OnExit()
{
if(g_hHook)
UnhookWindowsHookEx(g_hHook);
//卸載鉤子
exit(0);
}
至此,整個程序就編寫完成了。打開我們的程序,按下“開始探測”按鈕,再打開需要輸入密碼的程序試試,是不是以明文顯示了?用這個小程序可以搞定系統(tǒng)的密碼設置、Outlook或防范不嚴的程序密碼,但對有專門防范的程序就不行了,比如新版QQ。