分享
 
 
 

超类化

王朝c/c++·作者佚名  2006-01-06
窄屏简体版  字體: |||超大  

在这一讲我们将学习什么是超类化以及它有什么作用;同时你还会学到怎样在自己的窗口中用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 的处理过程和这正好相反。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有