程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 匯編教程之窗口子類化

匯編教程之窗口子類化

編輯:匯編語言

在這一講,我們將學習什麼是窗口子類化和怎樣按你所想要的方式方便地使用它。

理論:

如果你曾經在 Windows 環境下編過程序,有時候就會發現:有一個現成的窗口,幾乎有你所需要的全部功能,但還不完全一樣(否則就沒有必要講這一節了)。你曾遇到過這樣的處境嗎,如果你需要一個具有過濾特殊字符功能的 Edit 控件。當然最直接的方法就是自己用代碼來實現,但這的確是一個費時又很困難的任務,而窗口子類化就可以用來做這種事情。

窗口子類化允許你接管被子類化的窗口,使你對它有絕對的控制權。舉個例子了來闡明一下:例如你需要一個只接受十六進制數字輸入的文本編輯框,如果使用一個簡單的 Edit控件,當用戶輸入十六進制以外的字符時,你既不知道也無計可施。也就是說,當用戶進文本框中輸入字符串 "zb+q*" 時,如果除了拒絕接受整個字符串以外幾乎什麼也不能做,至少這顯得特別不專業。重要的是,你需要具有輸入檢測的能力,即每當用戶輸入一個字符到編輯框中時要能檢測這個字符。

現在來解釋實現細節:當用戶往文本框中輸入字符時,Windows 會給Edit控件的窗口函數發送 WM_CHAR 消息。這個窗口函數本身寄生於 Windows 中,因此不能直接修改它。但是我們可以重定向這個消息使之發送到我們自己編寫的窗口處理函數。如果自定義窗口要處理這個消息那就可以處理它,如果不處理就可以把這個消息轉發到它原來窗口處理函數。通過這種方式,自定義的窗口處理函數就把它自己插入到 Windows 系統和 Edit 控件之間。

看下面的流程:

窗口子類化之前

Windows ==>Edit 控件的窗口處理函數。

子類化之後

Windows ==>自定義的窗口處理函數==> Edit 控件的窗口處理函數。

注意子類化並不局限於控件,可以子類化任何窗口,現在我們要把精力集中到怎樣實現子類化一個窗口上。讓我們想想Windows 怎樣知道 Edit 控件的窗口處理函數放在什麼地方。猜的?…肯定不是。原來 WNDCLASSEX 結構的成員 lpfnWndProc 指出了窗口函數地址。如果能用自己編寫的窗口函數的地址來替換這個成員變量,那 Windows 不就把消息發到自定義的窗口函數了嗎! 我們通過調用函數SetWindowLong 來實現這個任務,此函數的原型為:

SetWindowLong PROTO hWnd:DWORD, nIndex:DWORD, dwNewLong:DWORD

hWnd = 將要實施子類化的窗口的句柄

nIndex = 函數了功能索引

GWL_EXSTYLE 設置窗口的擴展風格.

GWL_STYLE 設置新的窗口風格

GWL_WNDPROC 設置新的窗口處理函數地址

GWL_HINSTANCE 設置新的應用程序句柄

GWL_ID 設置新的窗口標識

GWL_USERDATA 設置一個與這個窗口相關的給用戶使用的32位的數據

dwNewLong = 用來更新的數據

我們的工作還是比較簡單的:

寫一個窗口函數用於處理發給 Edit 控件的消息。

用參數GWL_WNDPROC調用SetWindowLong 函數,如果調用成功那麼返回值就是與調用功能相聯系的一個32位的整數

在我們的程序中,返回值就是原先窗口函數的地址。我們要保存這個值以便以後使用。 記住:有一些我們不處理的消息,需要把它們派遣給原來的窗口函數來處理,這就用到另外一個函數 CallWindowProc, 函數原型為:

CallWindowProc PROTO lpPrevWndFunc:DWORD, hWnd:DWORD, Msg:DWORD, wParam:DWORD, lParam:DWORD

lpPrevWndFunc = 窗口原來函數的地址. 剩下的四個參數就是發給自定義函數的參數,直接把它們傳給函數 CallWindowProc 就行了。

代碼舉例:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "SubclassWinClass",0
AppName db "Subclassing Demo",0
EditClass db "EDIT",0
Message db "You pressed Enter in the text box!",0
.data?
hInstance HINSTANCE ?
hwndEdit dd ?
OldWndProc dd ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_APPWORKSPACE
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
CW_USEDEFAULT,350,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
.while TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR EditClass,NULL,\
WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
20,300,25,hWnd,NULL,\
hInstance,NULL
mov hwndEdit,eax
invoke SetFocus,eax
;-----------------------------------------
; Subclass it!
;-----------------------------------------
invoke SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
mov OldWndProc,eax
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
EditWndProc PROC hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
.if uMsg==WM_CHAR
mov eax,wParam
.if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
.if al>="a" && al<="f"
sub al,20h
.endif
invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam
ret
.endif
.elseif uMsg==WM_KEYDOWN
mov eax,wParam
.if al==VK_RETURN
invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
invoke SetFocus,hEdit
.else
invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
ret
.endif
.else
invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
EditWndProc endp
end start

分析:

invoke SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
mov OldWndProc,eax

在創建 Edit 控件後,通過調用 SetWindowLong 把原來的窗口函數地址替換為自定義函數的地址,從而對它實施了窗口子類化,要注意 為了調用函數 CallWindowProc,我們存儲了原窗口函數地址,自已編寫的EditWndProc 僅僅是個普普通通的窗口函數。當然也可以再調用一次 SetWindowLong 函數來存儲這個32位的值,

invoke SetWindowLong ,hwndEdit,GWL_USERDATA,eax 。

當然用的時候就要調用GetWindowLong 來取回這個值。

.if uMsg==WM_CHAR
mov eax,wParam
.if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
.if al>="a" && al<="f"
sub al,20h
.endif
invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam
ret
.endif

在函數 EditWndProc 中,我們自己處理了WM_CHAR消息: 如果輸入的字符是'0'--'9'、'A'-'F'或者是'a'--'f'就接受,並且把此消息轉發給原窗口函數,其中若輸入的是小寫的'a'--'f'就把它變為大寫。如果輸入的不是十六進制字符,就丟掉它,並且也不轉發此消息。因此當輸入是非十六進制字符時,這個字符就不會顯示在 Edit 控件中。

.elseif uMsg==WM_KEYDOWN
mov eax,wParam
.if al==VK_RETURN
invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
invoke SetFocus,hEdit
.else
invoke CallWindowProc,OldWndProc,hEdit,uMsg,wParam,lParam
ret
.end

在這裡我們通過處理 回車(Enter) 鍵進一步示范了子類化的能力。EditWndProc 通過檢查 WM_KEYDONW 消息來判斷是否是 回車鍵,若是顯示提示消息框,否則轉發此消息。 你可以用窗口子類化來控制另外的窗口,這是必須掌握的十分有用的技術之一。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved