在這一講我們將學(xué)習(xí)什么是超類化以及它有什么作用;同時(shí)你還會(huì)學(xué)到怎樣在自己的窗口中用Tab鍵在控件中切換這一技巧。
理論:
在你的程序生涯中你肯定遇到過(guò)這樣的情況,你需要一系列的控件,但它們之間卻只有一點(diǎn)點(diǎn)的不同。例如,你可能需要10個(gè)只接受數(shù)字的 Edit 控件,當(dāng)然你可以通過(guò)多種方法來(lái)達(dá)到這個(gè)目的。
創(chuàng)建自己的類并用它實(shí)例化為那些控件
創(chuàng)建那些 Edit 控件并把它們?nèi)孔宇惢?BR> 超類化Edit 控件
第一種方法太乏味了,因?yàn)槟惚仨氉约簩?shí)現(xiàn)Edit 控件的每個(gè)功能,但這項(xiàng)工作不是輕松就能完成的。第二種方法好于第一種,但仍然要做許多工作,子類化幾個(gè)Edit 控件還可以接受,但若要子類化十幾二十個(gè),這項(xiàng)工作簡(jiǎn)直就是一場(chǎng)惡夢(mèng)。在這種情況下就應(yīng)該使用超類化這個(gè)技巧,它是 用于控制某一個(gè)特定窗口類的特殊方法。通過(guò)這種控制就可以修改窗口類的特性使之符合你的要求,然后再創(chuàng)建那一堆控件就可以了。
超類化有如下幾個(gè)步驟:
通過(guò)調(diào)用 GetClassInfoEx 來(lái)獲得想要進(jìn)行超類化操作的窗口類的信息。函數(shù)GetClassInfoEx 需要一個(gè)指向 WNDCLASSEX 結(jié)構(gòu)的指針,用于當(dāng)成功返回時(shí)填入窗口類的信息。
按需要修改 WNDCLASSEX 結(jié)構(gòu)的成員,其中有兩個(gè)成員必須修改:
hInstance 存放程序的實(shí)例句柄
lpszClassName 指向一個(gè)新類名的指針
不必修改成員 lpfnWndProc,但大多數(shù)情況下還是需要的。但要記住如果要使用函數(shù) CallWindowProc 調(diào)用老窗口的過(guò)程,那就必須保存成員 lpfnWndProc 的原值。
注冊(cè)修改完的 WNDCLASSEX 結(jié)構(gòu),得到一個(gè)具有舊窗口類某些特性的新窗口類。
用新窗口類創(chuàng)建窗口
如果要?jiǎng)?chuàng)建具有相同特性的多個(gè)控件,超類化就比子類化要好。
舉例:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WM_SUPERCLASS equ WM_USER+5
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "SuperclassWinClass",0
AppName db "Superclassing Demo",0
EditClass db "EDIT",0
OurClass db "SUPEREDITCLASS",0
Message db "You pressed the Enter key in the text box!",0
.data?
hInstance dd ?
hwndEdit dd 6 dup(?) ;存放6個(gè)窗口句柄的數(shù)組
OldWndProc dd ? ;原來(lái)的窗口過(guò)程
.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+WS_EX_CONTROLPARENT,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
CW_USEDEFAULT,350,220,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 uses ebx edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL wc:WNDCLASSEX
.if uMsg==WM_CREATE
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr EditClass,addr wc
push wc.lpfnWndProc
pop OldWndProc
mov wc.lpfnWndProc, OFFSET EditWndProc
push hInstance
pop wc.hInstance
mov wc.lpszClassName,OFFSET OurClass
invoke RegisterClassEx, addr wc
xor ebx,ebx
mov edi,20
.while ebx<6
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\
WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
edi,300,25,hWnd,ebx,\
hInstance,NULL
mov dword ptr [hwndEdit+4*ebx],eax
add edi,25
inc ebx
.endw
invoke SetFocus,hwndEdit
.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
;處理字符0~9,A~F,a~f,這幾個(gè)十六進(jìn)制數(shù)
.if al>="a" && al<="f"
sub al,20h
如果是字符a~f,則把它們變?yōu)榇髮?BR> .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
.elseif al==VK_TAB
invoke GetKeyState,VK_SHIFT
test eax,80000000
.if ZERO?
invoke GetWindow,hEdit,GW_HWNDNEXT
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDFIRST
.endif
.else
invoke GetWindow,hEdit,GW_HWNDPREV
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDLAST
.endif
.endif
invoke SetFocus,eax
xor eax,eax
ret
.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
分析
這個(gè)程序創(chuàng)建了一個(gè)在其客戶區(qū)有六個(gè)被修改的 Edit 控件的簡(jiǎn)單窗口,這些 Edit控件只接受十六進(jìn)制的數(shù)字。實(shí)際上,這個(gè)例子是通過(guò)修改窗口了類化的例子得來(lái)的。這個(gè)程序開始和其它程序一樣,有趣的部分出現(xiàn)在主窗口被創(chuàng)建的時(shí)候:
.if uMsg==WM_CREATE
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr EditClass,addr wc
必須用想進(jìn)行超類化操作的類數(shù)據(jù)填充 WNDCLASSEX 結(jié)構(gòu),在我們的例子中就是類 Edit ,記住在調(diào)用函數(shù) GetClassInfoEx 之前必須填寫成員 cbSize,否則函數(shù)調(diào)用 GetClassInfoEx不會(huì)在 WNDCLASSEX 結(jié)構(gòu)中填入正確的返回值。成功返回后,變量 wc中保存的就是想要?jiǎng)?chuàng)建一個(gè)新類所需要的所有信息。
push wc.lpfnWndProc
pop OldWndProc
mov wc.lpfnWndProc, OFFSET EditWndProc
push hInstance
pop wc.hInstance
mov wc.lpszClassName,OFFSET OurClass
現(xiàn)在必須修改變量 wc 的一些屬性:第一個(gè)要修改的就是指向窗口過(guò)程的指針。因?yàn)樵谛麓翱谶^(guò)程中函數(shù) CallWindowProx 要用到老窗口過(guò)程,因此得把它保存到一個(gè)變量中以便使用。這個(gè)技巧和在子類化中用到的一樣,只不過(guò)不是調(diào)用 SetWindowLong 而是直接修改 WNDCLASSEX 結(jié)構(gòu)罷了。接下來(lái)必須得為這個(gè)新類取個(gè)名字。
invoke RegisterClassEx, addr wc
當(dāng)所有這些都完成時(shí),注冊(cè)這個(gè)新類就會(huì)得到一個(gè)具有舊類某些特征的新類了。
xor ebx,ebx
mov edi,20
.while ebx<6
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\
WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
edi,300,25,hWnd,ebx,\
hInstance,NULL
mov dword ptr [hwndEdit+4*ebx],eax
add edi,25
inc ebx
.endw
invoke SetFocus,hwndEdit
注冊(cè)完新類就可以創(chuàng)建基于它的窗口了:
在上面的程序片斷中,用寄存器 ebx 來(lái)保存已創(chuàng)建的窗口數(shù)目,用寄存器 edi 來(lái)保存窗口左上角的 y 坐標(biāo)。創(chuàng)建一個(gè)新窗口時(shí),把它的句柄保存在一個(gè)雙字的數(shù)組中,當(dāng)創(chuàng)建完所有的窗口后,設(shè)定輸入焦點(diǎn)為所創(chuàng)建的第一個(gè)窗口。
這時(shí)已經(jīng)有6個(gè)只能接受十六進(jìn)制數(shù)字的 edit 窗口控件了,替換的窗口過(guò)程處理了字符過(guò)濾,這實(shí)際上和在子類化中的例子是一樣的。但不必做子類化那些窗口的額外工作了。
在此程序中,通過(guò)使用 Tabs 鍵來(lái)在各個(gè) Edit 控件中切換來(lái)使得這個(gè)程序更加有趣。一般來(lái)說(shuō),如果使用對(duì)話框,對(duì)話框管理器會(huì)處理好所有這些問(wèn)題,即:
按下 Tabs 輸入焦點(diǎn)切換到下一個(gè)控件窗口中,按下 Shift-Tabs 輸入焦點(diǎn)切換到上一個(gè)控件窗口中;但一個(gè)簡(jiǎn)單的窗口不具有這個(gè)功能,必須子類化它們以處理 Tabs 鍵。在這個(gè)例子中,不必一個(gè)一個(gè)去子類化已經(jīng)進(jìn)行過(guò)超類化操作的這些控件,可以使用一種集中控制切換策略。
.elseif al==VK_TAB
invoke GetKeyState,VK_SHIFT
test eax,80000000
.if ZERO?
invoke GetWindow,hEdit,GW_HWNDNEXT
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDFIRST
.endif
.else
invoke GetWindow,hEdit,GW_HWNDPREV
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDLAST
.endif
.endif
invoke SetFocus,eax
xor eax,eax
ret
上面是摘自于 EditWndClass 過(guò)程的程序片斷,它檢查用戶是否按下了 Tabs 鍵,若是就調(diào)用函數(shù) GetKeyState 來(lái)檢查 SHIFT 鍵是否也被同時(shí)按下了。函數(shù) GetKeyState 在寄存器 eax 中設(shè)立一個(gè)返回值,用于判斷某個(gè)特定的鍵是否被按下了,若按下了,則把 eax 的的位置1,否則把位清0。所以只要用 80000000h 來(lái)測(cè)試返回值就行了,若位是1則說(shuō)明用戶按下了 SHIFT-Tabs,這需要單獨(dú)處理;否則說(shuō)明只按下 Tabs 鍵,調(diào)用函數(shù) GetWindow 來(lái)獲得 hEdit 所指向窗口的下一個(gè)窗口句柄,若該函數(shù)返回 NULL ,說(shuō)明這是當(dāng)前窗口是窗口鏈中最后一個(gè)窗口了,應(yīng)該通過(guò)以參數(shù) GW_HWNDFIRST 調(diào)用函數(shù) GetWindow 來(lái)卷回到窗口鏈中的第一個(gè)窗口控件。SHIFT-Tabs 的處理過(guò)程和這正好相反。
理論:
在你的程序生涯中你肯定遇到過(guò)這樣的情況,你需要一系列的控件,但它們之間卻只有一點(diǎn)點(diǎn)的不同。例如,你可能需要10個(gè)只接受數(shù)字的 Edit 控件,當(dāng)然你可以通過(guò)多種方法來(lái)達(dá)到這個(gè)目的。
創(chuàng)建自己的類并用它實(shí)例化為那些控件
創(chuàng)建那些 Edit 控件并把它們?nèi)孔宇惢?BR> 超類化Edit 控件
第一種方法太乏味了,因?yàn)槟惚仨氉约簩?shí)現(xiàn)Edit 控件的每個(gè)功能,但這項(xiàng)工作不是輕松就能完成的。第二種方法好于第一種,但仍然要做許多工作,子類化幾個(gè)Edit 控件還可以接受,但若要子類化十幾二十個(gè),這項(xiàng)工作簡(jiǎn)直就是一場(chǎng)惡夢(mèng)。在這種情況下就應(yīng)該使用超類化這個(gè)技巧,它是 用于控制某一個(gè)特定窗口類的特殊方法。通過(guò)這種控制就可以修改窗口類的特性使之符合你的要求,然后再創(chuàng)建那一堆控件就可以了。
超類化有如下幾個(gè)步驟:
通過(guò)調(diào)用 GetClassInfoEx 來(lái)獲得想要進(jìn)行超類化操作的窗口類的信息。函數(shù)GetClassInfoEx 需要一個(gè)指向 WNDCLASSEX 結(jié)構(gòu)的指針,用于當(dāng)成功返回時(shí)填入窗口類的信息。
按需要修改 WNDCLASSEX 結(jié)構(gòu)的成員,其中有兩個(gè)成員必須修改:
hInstance 存放程序的實(shí)例句柄
lpszClassName 指向一個(gè)新類名的指針
不必修改成員 lpfnWndProc,但大多數(shù)情況下還是需要的。但要記住如果要使用函數(shù) CallWindowProc 調(diào)用老窗口的過(guò)程,那就必須保存成員 lpfnWndProc 的原值。
注冊(cè)修改完的 WNDCLASSEX 結(jié)構(gòu),得到一個(gè)具有舊窗口類某些特性的新窗口類。
用新窗口類創(chuàng)建窗口
如果要?jiǎng)?chuàng)建具有相同特性的多個(gè)控件,超類化就比子類化要好。
舉例:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WM_SUPERCLASS equ WM_USER+5
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
EditWndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName db "SuperclassWinClass",0
AppName db "Superclassing Demo",0
EditClass db "EDIT",0
OurClass db "SUPEREDITCLASS",0
Message db "You pressed the Enter key in the text box!",0
.data?
hInstance dd ?
hwndEdit dd 6 dup(?) ;存放6個(gè)窗口句柄的數(shù)組
OldWndProc dd ? ;原來(lái)的窗口過(guò)程
.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+WS_EX_CONTROLPARENT,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
CW_USEDEFAULT,350,220,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 uses ebx edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL wc:WNDCLASSEX
.if uMsg==WM_CREATE
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr EditClass,addr wc
push wc.lpfnWndProc
pop OldWndProc
mov wc.lpfnWndProc, OFFSET EditWndProc
push hInstance
pop wc.hInstance
mov wc.lpszClassName,OFFSET OurClass
invoke RegisterClassEx, addr wc
xor ebx,ebx
mov edi,20
.while ebx<6
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\
WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
edi,300,25,hWnd,ebx,\
hInstance,NULL
mov dword ptr [hwndEdit+4*ebx],eax
add edi,25
inc ebx
.endw
invoke SetFocus,hwndEdit
.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
;處理字符0~9,A~F,a~f,這幾個(gè)十六進(jìn)制數(shù)
.if al>="a" && al<="f"
sub al,20h
如果是字符a~f,則把它們變?yōu)榇髮?BR> .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
.elseif al==VK_TAB
invoke GetKeyState,VK_SHIFT
test eax,80000000
.if ZERO?
invoke GetWindow,hEdit,GW_HWNDNEXT
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDFIRST
.endif
.else
invoke GetWindow,hEdit,GW_HWNDPREV
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDLAST
.endif
.endif
invoke SetFocus,eax
xor eax,eax
ret
.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
分析
這個(gè)程序創(chuàng)建了一個(gè)在其客戶區(qū)有六個(gè)被修改的 Edit 控件的簡(jiǎn)單窗口,這些 Edit控件只接受十六進(jìn)制的數(shù)字。實(shí)際上,這個(gè)例子是通過(guò)修改窗口了類化的例子得來(lái)的。這個(gè)程序開始和其它程序一樣,有趣的部分出現(xiàn)在主窗口被創(chuàng)建的時(shí)候:
.if uMsg==WM_CREATE
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr EditClass,addr wc
必須用想進(jìn)行超類化操作的類數(shù)據(jù)填充 WNDCLASSEX 結(jié)構(gòu),在我們的例子中就是類 Edit ,記住在調(diào)用函數(shù) GetClassInfoEx 之前必須填寫成員 cbSize,否則函數(shù)調(diào)用 GetClassInfoEx不會(huì)在 WNDCLASSEX 結(jié)構(gòu)中填入正確的返回值。成功返回后,變量 wc中保存的就是想要?jiǎng)?chuàng)建一個(gè)新類所需要的所有信息。
push wc.lpfnWndProc
pop OldWndProc
mov wc.lpfnWndProc, OFFSET EditWndProc
push hInstance
pop wc.hInstance
mov wc.lpszClassName,OFFSET OurClass
現(xiàn)在必須修改變量 wc 的一些屬性:第一個(gè)要修改的就是指向窗口過(guò)程的指針。因?yàn)樵谛麓翱谶^(guò)程中函數(shù) CallWindowProx 要用到老窗口過(guò)程,因此得把它保存到一個(gè)變量中以便使用。這個(gè)技巧和在子類化中用到的一樣,只不過(guò)不是調(diào)用 SetWindowLong 而是直接修改 WNDCLASSEX 結(jié)構(gòu)罷了。接下來(lái)必須得為這個(gè)新類取個(gè)名字。
invoke RegisterClassEx, addr wc
當(dāng)所有這些都完成時(shí),注冊(cè)這個(gè)新類就會(huì)得到一個(gè)具有舊類某些特征的新類了。
xor ebx,ebx
mov edi,20
.while ebx<6
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\
WS_CHILD+WS_VISIBLE+WS_BORDER,20,\
edi,300,25,hWnd,ebx,\
hInstance,NULL
mov dword ptr [hwndEdit+4*ebx],eax
add edi,25
inc ebx
.endw
invoke SetFocus,hwndEdit
注冊(cè)完新類就可以創(chuàng)建基于它的窗口了:
在上面的程序片斷中,用寄存器 ebx 來(lái)保存已創(chuàng)建的窗口數(shù)目,用寄存器 edi 來(lái)保存窗口左上角的 y 坐標(biāo)。創(chuàng)建一個(gè)新窗口時(shí),把它的句柄保存在一個(gè)雙字的數(shù)組中,當(dāng)創(chuàng)建完所有的窗口后,設(shè)定輸入焦點(diǎn)為所創(chuàng)建的第一個(gè)窗口。
這時(shí)已經(jīng)有6個(gè)只能接受十六進(jìn)制數(shù)字的 edit 窗口控件了,替換的窗口過(guò)程處理了字符過(guò)濾,這實(shí)際上和在子類化中的例子是一樣的。但不必做子類化那些窗口的額外工作了。
在此程序中,通過(guò)使用 Tabs 鍵來(lái)在各個(gè) Edit 控件中切換來(lái)使得這個(gè)程序更加有趣。一般來(lái)說(shuō),如果使用對(duì)話框,對(duì)話框管理器會(huì)處理好所有這些問(wèn)題,即:
按下 Tabs 輸入焦點(diǎn)切換到下一個(gè)控件窗口中,按下 Shift-Tabs 輸入焦點(diǎn)切換到上一個(gè)控件窗口中;但一個(gè)簡(jiǎn)單的窗口不具有這個(gè)功能,必須子類化它們以處理 Tabs 鍵。在這個(gè)例子中,不必一個(gè)一個(gè)去子類化已經(jīng)進(jìn)行過(guò)超類化操作的這些控件,可以使用一種集中控制切換策略。
.elseif al==VK_TAB
invoke GetKeyState,VK_SHIFT
test eax,80000000
.if ZERO?
invoke GetWindow,hEdit,GW_HWNDNEXT
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDFIRST
.endif
.else
invoke GetWindow,hEdit,GW_HWNDPREV
.if eax==NULL
invoke GetWindow,hEdit,GW_HWNDLAST
.endif
.endif
invoke SetFocus,eax
xor eax,eax
ret
上面是摘自于 EditWndClass 過(guò)程的程序片斷,它檢查用戶是否按下了 Tabs 鍵,若是就調(diào)用函數(shù) GetKeyState 來(lái)檢查 SHIFT 鍵是否也被同時(shí)按下了。函數(shù) GetKeyState 在寄存器 eax 中設(shè)立一個(gè)返回值,用于判斷某個(gè)特定的鍵是否被按下了,若按下了,則把 eax 的的位置1,否則把位清0。所以只要用 80000000h 來(lái)測(cè)試返回值就行了,若位是1則說(shuō)明用戶按下了 SHIFT-Tabs,這需要單獨(dú)處理;否則說(shuō)明只按下 Tabs 鍵,調(diào)用函數(shù) GetWindow 來(lái)獲得 hEdit 所指向窗口的下一個(gè)窗口句柄,若該函數(shù)返回 NULL ,說(shuō)明這是當(dāng)前窗口是窗口鏈中最后一個(gè)窗口了,應(yīng)該通過(guò)以參數(shù) GW_HWNDFIRST 調(diào)用函數(shù) GetWindow 來(lái)卷回到窗口鏈中的第一個(gè)窗口控件。SHIFT-Tabs 的處理過(guò)程和這正好相反。