庞大资源库的计算机教程网站!
设为首页
加入收藏
总编信箱
投稿或申请专栏请先 [登 陆]
首页 操作系统 程序设计 图形图像 媒体动画 机械电子 WEB开发 数 据 库 办公系列 路由技术 网络原理 网络应用
认证考试 安全技术
首页>程序设计>汇编语言>基础教程>正文
资料搜索
Google搜索
Google
返回上级列表

推荐文章

快速保存网页中所有图片的方法
Windows中让光驱巧妙“隐身”技
防范非法用户入侵Win 2000/XP系
两款比较典型的ASP木马防范方法
有关表格边框的css语法整理
Windows XP中可以被禁用的服务
SQL Server导出导入数据方法
Javascript所有对象的属性的获
网页(HTML)中的特殊字符
与篮球共舞,尽显模式本色
QQ病毒的手工清除方法
Photoshop为极品美女打造性感睫
天衣无缝:IIS与PHP水火也相容
SQL Server存储过程编写和优化

超类化

 作者:本站收集   日期:2005-8-1 15:27:49
字号选择〖 〗/ 双击滚屏 单击停止   

  在这一讲我们将学习什么是超类化以及它有什么作用;同时你还会学到怎样在自己的窗口中用Tab键在控件中切换这一技巧

理论:

在你的程序生涯中你肯定遇到过这样的情况,你需要一系列的控件,但它们之间却只有一点点的不同。例如,你可能需要10个只接受数字的 Edit 控件,当然你可以通过多种方法来达到这个目的。

创建自己的类并用它实例化为那些控件
创建那些 Edit 控件并把它们全部子类化
超类化Edit 控件
第一种方法太乏味了,因为你必须自己实现Edit 控件的每个功能,但这项工作不是轻松就能完成的。第二种方法好于第一种,但仍然要做许多工作,子类化几个Edit 控件还可以接受,但若要子类化十几二十个,这项工作简直就是一场恶梦。在这种情况下就应该使用超类化这个技巧,它是用于控制某一个特定窗口类的特殊方法。通过这种控制就可以修改窗口类的特性使之符合你的要求,然后再创建那一堆控件就可以了。

超类化有如下几个步骤:

通过调用 GetClassInfoEx 来获得想要进行超类化操作的窗口类的信息。函数GetClassInfoEx 需要一个指向 WNDCLASSEX 结构的指针,用于当成功返回时填入窗口类的信息。
按需要修改 WNDCLASSEX 结构的成员,其中有两个成员必须修改:
hInstance 存放程序的实例句柄
lpszClassName 指向一个新类名的指针
不必修改成员 lpfnWndProc,但大多数情况下还是需要的。但要记住如果要使用函数 CallWindowProc 调用老窗口的过程,那就必须保存成员 lpfnWndProc 的原值。
注册修改完的 WNDCLASSEX 结构,得到一个具有旧窗口类某些特性的新窗口类。
用新窗口类创建窗口
如果要创建具有相同特性的多个控件,超类化就比子类化要好。

举例:

.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个窗口句柄的数组
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+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,这几个十六进制数
.if al>="a" && al<="f"
sub al,20h
如果是字符a~f,则把它们变为大写
.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


分析

这个程序创建了一个在其客户区有六个被修改的 Edit 控件的简单窗口,这些 Edit控件只接受十六进制的数字。实际上,这个例子是通过修改窗口了类化的例子得来的。这个程序开始和其它程序一样,有趣的部分出现在主窗口被创建的时候:

.if uMsg==WM_CREATE
mov wc.cbSize,sizeof WNDCLASSEX
invoke GetClassInfoEx,NULL,addr EditClass,addr wc

必须用想进行超类化操作的类数据填充 WNDCLASSEX 结构,在我们的例子中就是类 Edit ,记住在调用函数 GetClassInfoEx 之前必须填写成员 cbSize,否则函数调用 GetClassInfoEx不会在 WNDCLASSEX 结构中填入正确的返回值。成功返回后,变量 wc中保存的就是想要创建一个新类所需要的所有信息。

push wc.lpfnWndProc
pop OldWndProc
mov wc.lpfnWndProc, OFFSET EditWndProc
push hInstance
pop wc.hInstance
mov wc.lpszClassName,OFFSET OurClass

现在必须修改变量 wc 的一些属性:第一个要修改的就是指向窗口过程的指针。因为在新窗口过程中函数 CallWindowProx 要用到老窗口过程,因此得把它保存到一个变量中以便使用。这个技巧和在子类化中用到的一样,只不过不是调用 SetWindowLong 而是直接修改 WNDCLASSEX 结构罢了。接下来必须得为这个新类取个名字。

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

注册完新类就可以创建基于它的窗口了:
在上面的程序片断中,用寄存器 ebx 来保存已创建的窗口数目,用寄存器 edi 来保存窗口左上角的 y 坐标。创建一个新窗口时,把它的句柄保存在一个双字的数组中,当创建完所有的窗口后,设定输入焦点为所创建的第一个窗口。

这时已经有6个只能接受十六进制数字的 edit 窗口控件了,替换的窗口过程处理了字符过滤,这实际上和在子类化中的例子是一样的。但不必做子类化那些窗口的额外工作了。

在此程序中,通过使用 Tabs 键来在各个 Edit 控件中切换来使得这个程序更加有趣。一般来说,如果使用对话框,对话框管理器会处理好所有这些问题,即:
按下 Tabs 输入焦点切换到下一个控件窗口中,按下 Shift-Tabs 输入焦点切换到上一个控件窗口中;但一个简单的窗口不具有这个功能,必须子类化它们以处理 Tabs 键。在这个例子中,不必一个一个去子类化已经进行过超类化操作的这些控件,可以使用一种集中控制切换策略。

.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 过程的程序片断,它检查用户是否按下了 Tabs 键,若是就调用函数 GetKeyState 来检查 SHIFT 键是否也被同时按下了。函数 GetKeyState 在寄存器 eax 中设立一个返回值,用于判断某个特定的键是否被按下了,若按下了,则把 eax 的的最高位置1,否则把最高位清0。所以只要用 80000000h 来测试返回值就行了,若最高位是1则说明用户按下了 SHIFT-Tabs,这需要单独处理;否则说明只按下 Tabs 键,调用函数 GetWindow 来获得 hEdit 所指向窗口的下一个窗口句柄,若该函数返回 NULL ,说明这是当前窗口是窗口链中最后一个窗口了,应该通过以参数 GW_HWNDFIRST 调用函数 GetWindow 来卷回到窗口链中的第一个窗口控件。SHIFT-Tabs 的处理过程和这正好相反。

上一篇:系统托盘中的快捷图标    下一篇:管道  
[发送给好友]  [关闭窗口]  [返回顶部]   转载请注明来源:www.it00.com   
特别声明: 本站除部分特别声明禁止转载的专稿外的其他文章可以自由转载,但请务必注明出处和原始作者。文章版权归文章原始作者所有。对于被本站转载文章的个人和网站,我们表示深深的谢意。如果本站转载的文章有版权问题请联系编辑人员,我们尽快予以更正。
责任编辑: 原点 投稿作者: 本站收集
信息来源: 网络 录入时间: 2005-8-1 15:27:49
关于我们 - 广告服务 - 版权申明 - 网站地图 - 联系方式 - 总编信箱 - 会员投稿