三级PC辅导:超类化

三级PC辅导:超类化,第1张

三级PC辅导:超类化,第2张

在这节课中,我们将学习什么是超分类以及它的作用。同时,您还将学习如何使用Tab键在自己的窗口中切换控件。

理论:

你在编程生涯中一定遇到过这种情况,你需要一系列的控件,但它们之间只有一点点区别。例如,您可能需要10个只接受数字的编辑控件。当然,你可以用很多方法做到这一点。

创建自己的类并为那些控件实例化
创建那些编辑控件并将其全部子类化
超类编辑控件
第一种方法太枯燥了,因为编辑控件的每一个功能都要自己实现,但这项工作并不能轻松完成。第二种方法比第一种好,但仍有许多工作要做。子类化几个编辑控件是可以接受的,但是子类化十几二十个控件是一场噩梦。在这种情况下,我们应该使用超分类技术,这是一种控制特定窗口类的特殊方法。通过这个控件,你可以修改窗口类的特性来满足你的需求,然后创建一堆控件。

超分类有以下步骤:

通过调用GetClassInfo OEX获得你想要超类的窗口类的信息。GetClassInfoEx函数需要一个指向WNDCLASSEX结构的指针,用于在成功返回时填充窗口类的信息。
根据需要修改WNDCLASSEX结构的成员,其中两个成员必须修改:
h instance存储程序的实例句柄
lpszclassname的指针指向一个新的类名
不需要修改成员lpfnWndProc,但大多数情况下仍有必要。但是要记住,如果要用CallWindowProc函数调用旧的窗口过程,必须保存成员lpfnWndProc的初始值。
注册修改后的WNDCLASSEX结构,得到一个新的窗口类,具有旧窗口类的一些特征。
用新的窗口类创建一个窗口
如果你想创建多个具有相同特征的控件,超类化比子类化更好。

示例:

. 386
。model flat,stdcall
option casemap:none
include \ masm 32 \ include \ windows . Inc
include \ masm 32 \ include \ USER 32 . Inc
include \ kernel 32 . Inc
include lib \ masm 32 \ lib \ USER 32 . lib
include lib \ masm 32 \ lib \ kernel 32 . lib
WM _ super class equ WM _ USER+

。data
class name db " super classwinclass ",0
AppName db " super classing Demo ",0
EditClass db "EDIT ",0
OurClass db " super EDIT class ",0
Message db "您在文本框中按了Enter键!",0

。数据?
hInstance dd?
hwndEdit dd 6 dup(?) ;6个窗口句柄的数组
OldWndProc dd?;原始窗口过程

。code
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke WinMain,hInstance,NULL,NULL,SW _ show default
invoke exit process,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或CS _ VREDRAW
mov WC . lpfnwndproc,OFFSET WndProc
mov WC . cbclsextra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop WC . hinstance
mov WC . HBR background,COLOR_APPWORKSPACE
mov wc当TRUE
调用GetMessage,ADDR消息,NULL,0,0
。休息。如果(!eax)
调用TranslateMessage,ADDR消息
调用DispatchMessage,ADDR消息
。endw
mov eax,msg . wparam
ret
WinMain endp

WndProc proc使用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 h instance
pop WC . h instance
mov WC . lpsz class name,OFFSET our而ebx调用CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\
WS _ CHILD+WS _ VISIBLE+WS _ BORDER,20,\
edi,300,25,hWnd,ebx,\
hInstance,NULL
mov dword ptr[hwn edit+4 * ebx],eax
add edi,25
inc ebx
。endw
调用SetFocus,hwndEdit
。else if uMsg = = WM _ DESTROY
invoke PostQuitMessage,NULL
。else
调用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 = " A " & & al = " A " & & al;处理字符0~9,A~F,a~f,这些十六进制数
。If al > = "a"&&al subal,20h
如果是字符a~f,就大写
。Endif
Invoke调用uMsg,eax,lParam
ret
。endif
。else if uMsg = = WM _ KEYDOWN
mov eax,wParam
。if al==VK_RETURN
调用MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
调用SetFocus,hEdit
。elseif al==VK_TAB
调用GetKeyState,VK_SHIFT
test eax,80000000
。如果零呢?
调用GetWindow,hEdit,GW_HWNDNEXT
。if eax==NULL
调用GetWindow,hEdit,GW_HWNDFIRST
。endif
。else
调用GetWindow,hEdit,GW_HWNDPREV
。if eax==NULL
调用GetWindow,hEdit,GW_HWNDLAST
。endif
。endif
invoke SetFocus,eax
xor eax,eax
ret
。else
调用CallWindowProc、OldWndProc、hEdit、uMsg、wParam、lParam
ret
。endif
。else
调用CallWindowProc、OldWndProc、hEdit、uMsg、wParam、Lparam
ret
。endif
xor eax,eax
ret
edit wndproc endp
end start
分析

这个程序创建了一个简单的窗口,在它的客户区有六个修改过的编辑控件。这些编辑控件只接受十六进制数字。实际上,这个示例是通过修改窗口来创建类示例而获得的。这个程序像其他程序一样启动,有趣的部分出现在主窗口创建时:

。if uMsg = = WM _ CREATE
mov WC . cbsize,sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr EditClass,addr wc

WNDCLASSEX结构必须填充您想要超类的类数据,在我们的例子中是类编辑。请记住,在调用函数GetClassInfoEx之前,必须填写成员cbSize,否则函数调用GetClassInfoEx将不会在WNDCLASSEX结构中填写正确的返回值。成功返回后,创建新类所需的所有信息都保存在变量wc中。

push WC . lpfnwndproc
pop OldWndProc
mov WC . lpfnwndproc,OFFSET EditWndProc
push h instance
pop WC . h instance
mov WC . lpsz class name,OFFSET OurClass

现在必须修改变量wc的一些属性:首先要修改的是指向窗口过程的指针。因为CallWindowProx函数在新的窗口过程中使用旧的窗口过程,所以它必须保存在一个变量中以供使用。这种技术与子类化中使用的技术相同,除了不是调用SetWindowLong,而是直接修改WNDCLASSEX结构。接下来,我们必须给这个新类起一个名字。

调用RegisterClassEx,addr wc

当所有这些都完成后,注册这个新类将得到一个具有旧类某些特征的新类。

xor ebx,ebx
mov edi,20
。而ebx调用CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,\
WS _ CHILD+WS _ VISIBLE+WS _ BORDER,20,\
edi,300,25,hWnd,ebx,\
hInstance,NULL
mov dword ptr[hwn edit+4 * ebx],eax
add edi,25
inc ebx
。endw
调用SetFocus,hwndEdit

注册一个新类后,可以基于它创建一个窗口:
在上面的程序片段中,寄存器ebx用来保存创建的窗口的数量,寄存器edi用来保存窗口左上角的Y坐标。创建新窗口时,将其句柄保存在一个双字数组中。创建所有窗口后,将输入焦点设置到第一个创建的窗口。

此时,已经有六个编辑窗口控件只能接受十六进制数。替换窗口过程处理字符过滤,这实际上与子类化中的例子相同。但是不需要做额外的工作来划分这些窗口的子类。

在这个程序中,tab键用于在各种编辑控件之间切换,使这个程序更有趣。一般来说,如果使用对话框,对话框管理器会处理所有这些问题,即:
按tab切换到下一个控制窗口,按Shift-Tabs切换到上一个控制窗口;但是,一个简单的窗口没有这个功能,它们必须被子类化来处理tab键。在这个例子中,没有必要将这些已经被超类化的控件一个接一个地子类化,但是可以使用集中的控件切换策略。

。elseif al==VK_TAB
调用GetKeyState,VK_SHIFT
test eax,80000000
。如果零呢?
调用GetWindow,hEdit,GW_HWNDNEXT
。if eax==NULL
调用GetWindow,hEdit,GW_HWNDFIRST
。endif
。else
调用GetWindow,hEdit,GW_HWNDPREV
。if eax==NULL
调用GetWindow,hEdit,GW_HWNDLAST
。endif
。endif
调用SetFocus,eax
xor eax,eax
ret

上面是EditWndClass进程的一个程序片段,它检查用户是否按下了Tabs键,如果是,调用函数GetKeyState检查SHIFT键是否也同时被按下。GetKeyState函数在寄存器eax中设置一个返回值,用于确定是否按下了某个特定的键。如果是,eax的位被设置为1,否则,该位被清除为0。所以用80000000h来测试返回值就可以了。如果该位为1,则表示用户按下了SHIFT-Tabs,需要单独处理;否则意味着只按了Tabs键,调用函数GetWindow获取hEdit指向的窗口的下一个窗口句柄。如果函数返回NULL,说明当前窗口是窗口链中的最后一个窗口,应该通过调用带参数GW_HWNDFIRST的函数GetWindow回滚到窗口链中的第一个窗口控件。SHIFT-Tabs正好相反。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » 三级PC辅导:超类化

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情