PC技术辅导:窗口子类化

PC技术辅导:窗口子类化,第1张

PC技术辅导:窗口子类化,第2张

理论:

如果你曾经在Windows环境下写过一个程序,有时候你会发现有一个现成的窗口,里面几乎包含了你需要的所有功能,但又不完全一样(不然就没必要讲这一节了)。你有没有遇到过这样的情况,如果你需要一个具有过滤特殊字符功能的编辑控件?当然,最直接的方法是用代码实现,但这确实是一件费时费力的事情,而窗口子类化可以用来做这种事情。

子类化允许你接管子类化的窗口,给你绝对的控制权。例如,您需要一个只接受十六进制数字的文本编辑框。如果你使用一个简单的编辑控件,当用户输入除十六进制以外的字符时,你将一无所知,无事可做。也就是说,当用户在文本框中输入字符串“zb+q*”时,如果除了拒绝接受整个字符串之外,几乎别无选择,至少是特别不专业的。重要的是,你需要有输入检测的能力,也就是说,你应该能够在每次用户把一个字符输入编辑框的时候检测到它。

现在,我们来解释一下实现细节:当用户在文本框中输入字符时,Windows会向编辑控件的窗口函数发送WM_CHAR消息。这个窗口函数本身是寄生在Windows中的,所以不能直接修改。但是我们可以重定向这个消息,并将其发送到我们自己的窗口处理程序。如果自定义窗口想要处理此消息,它可以处理它。如果没有,它可以将这个消息转发给它原来的窗口处理程序。通过这种方式,自定义窗口处理程序将其自身插入到窗口系统和编辑控件之间。

看下面这个过程:
窗口被子类化之前
Windows = = >编辑控件的窗口处理函数。

在子类化
Windows == >自定义窗口处理程序== >编辑控件的窗口处理程序之后。

请注意,子类化不限于控件。您可以创建任何窗口的子类。现在我们必须关注如何创建窗口的子类。让我们考虑一下Windows是如何知道编辑控件的窗口处理程序放在哪里的。猜猜?...肯定不是。WNDCLASSEX结构成员LpfnWndProc指出了窗口函数地址。如果可以用自己的窗口函数的地址替换这个成员变量,那么Windows就不会把消息发送给自定义窗口函数了!我们通过调用函数SetWindowLong来实现这个任务,它的原型是:

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

HWnd =要子类化的窗口的句柄
nIndex =函数索引
GWL_EXSTYLE设置窗口的扩展样式。
GWL_STYLE设置新的窗口样式
GWL_WNDPROC设置新的窗口处理程序地址
Gwl _ hint设置新的应用程序句柄
GWL_ID设置新的窗口标识符
GWL_USERDATA为用户设置一个与此窗口相关的32位数据
dwNewLong =要更新的数据
我们的工作相对简单:

写一个窗口函数来处理发送到编辑控件的消息。
使用参数GWL_WNDPROC调用SetWindowLong函数。如果调用成功,返回值是一个与调用函数相关的32位整数。
在我们的程序中,返回值是原窗口函数的地址。我们希望保存这个值供以后使用。记住:有一些消息是我们不处理的,需要调度到原来的窗口函数中进行处理,所以使用了另一个函数CallWindowProc。该函数的原型是:

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

LpPrevWndFunc =窗口原始函数的地址。剩下的四个参数是发送给自定义函数的参数。只需将它们直接传递给CallWindowProc函数。
代码示例:


.386
。模型平面,stdcall
选项casemap:none
include \ masm 32 \ include \ windows . Inc
include \ masm 32 \ include \ user 32 . Inc
include \ kernel 32 . Inc
include \ masm 32 \ include \ com CTL 32 . Inc
include lib \ masm 32 \ lib \ com CTL 32 . lib
include lib \ masm 32 \ lib \ user 32

。data
class name db " subclass winclass ",0
AppName db "子类化演示",0
EditClass db "EDIT ",0
Message db "您在文本框中按了Enter!",0

。数据?
hInstance HINSTANCE?
hwn edit DD?
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当TRUE
调用GetMessage,ADDR消息,NULL,0,0
。休息。如果(!eax)
调用TranslateMessage,ADDR消息
调用DispatchMessage,ADDR消息
。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
;-
;子类化它!
;-
调用SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
mov OldWndProc,eax
。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。if al>="a" && al sub al,20h
。endif
invoke CallWindowProc,OldWndProc,hEdit,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
。else
调用CallWindowProc、OldWndProc、hEdit、uMsg、wParam、lParam
ret
。endif
。else
调用CallWindowProc、OldWndProc、hEdit、uMsg、wParam、lParam
ret
。endif
xor eax,eax
ret
EditWndProc endp
end start

分析:

调用SetWindowLong,hwndEdit,GWL_WNDPROC,addr EditWndProc
mov OldWndProc,eax

编辑控件创建后,通过调用SetWindowLong将原来的窗口函数地址替换为用户自定义函数的地址,使其成为子类。需要注意的是,为了调用函数CallWindowProc,我们存储的是原来的窗口函数地址,自己写的EditWndProc只是一个普通的窗口函数。当然,也可以再次调用SetWindowLong函数来存储这个32位值,

调用SetWindowLong,hwndEdit,GWL _用户数据,eax .

当然,在使用的时候,还得调用GetWindowLong来检索这个值。


。if uMsg==WM_CHAR
mov eax,wParam
。if (al>="0" && al="A" && al="a" && al。if al>="a" && al sub al,20h
。endif
invoke CallWindowProc,OldWndProc,hEdit,uMsg,eax,lParam
ret
。结束条件

在EditWndProc函数中,我们自己处理WM_CHAR消息:如果输入字符是' 0'-'9 ',' A'-'F '或' a'-'f ',我们将接受它并将此消息转发到原始窗口函数,其中如果输入字符是小写' a'-'f ',我们将把它更改为。如果输入不是十六进制字符,它将被丢弃,并且消息不会被转发。因此,当输入是非十六进制字符时,该字符不会显示在编辑控件中。

。else if uMsg = = WM _ KEYDOWN
mov eax,wParam
。if al==VK_RETURN
调用MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
调用SetFocus,hEdit
。else
调用CallWindowProc、OldWndProc、hEdit、uMsg、wParam、lParam
ret
。目标

在这里,我们通过处理回车键进一步展示了子类化的能力。EditWndProc通过检查WM_KEYDONW消息来确定它是否是enter键,如果它显示一个消息框,则转发该消息。您可以使用窗口子类化来控制其他窗口,这是您必须掌握的非常有用的技术之一。

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

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情