Visual Basic中的子类处理技术
安徽省马鞍山钢铁总公司
王加龙
----
Visual Basic不仅仅是Windows环境下运行的一种全方位的编程语言,而且与Windows有着非常紧密的联系。如许多Visual
Basic属性和事件与Windows属性和事件有着严格的"一对一"的关系。正因为如此,Visual
Basic成为一种强大的Windows编程工具。在利用Visual
Basic开发应用程序时,为了使程序具有自己鲜明的个性,常常要对Visual
Basic中的窗体和控件做一些必要的改动,本文就谈一谈实现这一改动的一种方法--子类处理。
一、子类处理的概念
----
⒈ 窗口
----
在Visual Basic中,窗体和控件代表了屏幕上的一个矩形区域,这个矩形区域在Windows中就叫做窗口。因此窗体是窗口,按钮同样也是一个窗口等。需要认识的是如果它在屏幕里,它是位于一个窗口里,如果它不在屏幕里,它仍然可能位于一个窗口里。
----
⒉ 窗口函数
----
任何一个窗口都属于某个特定的类,在窗口建立时,都要调用该类提供的一个默认的窗口函数,窗口函数的作用在于对消息进行处理。窗口函数有四个参数,第一个参数是窗口的句柄,第二个参数是消息的编号,第三和第四个参数是32位参数,它们依据不同的消息取不同的值。
----
⒊ 子类处理
----
窗口并不一定非要默认那个窗口函数,完全可以调用由用户自己编写的一个窗口函数,从而达到拦截和处理到达该窗口的Windows消息,并能调用原来默认的窗口函数,这样就可以对该窗口的所有运作进行定制。这种技术就叫做子类处理技术。
二、子类处理的实现方法
----
在Visual Basic环境下,为了实现子类处理,可以通过调用SetWindowLong这个API函数,用自己编写的窗口函数代替原来的窗口函数。SetWindowLong函数有三个参数,第一个是欲为其取得信息的窗口的句柄,第二个参数是欲取回的信息,在本方法中设置为GWL_WNDPROC,第三个参数是由第二个参数指定的窗口的新值,在本方法中应设置为自己编写的窗口函数的地址。在Visual
Basic5.0以上的版本中,窗口函数的地址可以通过AddressOf操作符获得。值得注意的是,第一,窗口函数必须是Visual
Basic工程中标准模块中的一个函数;第二,窗口必须是自己进程中的,若不是,则不能AddressOf操作符对它进行子类处理。为实现这种子类处理,必须采用第三方提供的控件或DLL。
----
窗口函数的编写是子类处理的关键。如上所述,窗口函数须有四个参数,并且能拦截和处理消息。消息的拦截可采用Select
Case语句,消息的处理可采用三种方式:
----
第一,把消息不做任何处理传给原来的窗口函数;
----
第二,把消息修改后再传给原来的函数;
----
第三,自己处理这条消息而不传给原来的窗口函数。窗口函数如果用Visual
Basic编写,其结构应该是如下的形式:
Public Function WindowProc(ByVal hWnd As Long, ByVal
sMessage AsLong, ByVal_ wParam As Long, ByVal lParam As
Long) As Long Select Case sMessage CasesMessage1'拦截sMessage1消息
'放置处理sMessage1消息的代码 CasesMessage2'拦截sMessage2消息
'放置处理sMessage2消息的代码 …………
………… End Select '调用窗口原来的窗口函数,处理不需要自己处理的消息
WindowProc=DefWindowProc (hWnd, sMessage, wParam, lParam)
End Function
三、一个简单的例子
----
在本例中,通过拦截到达窗体的WM_SETCURSOR消息,判断鼠标在窗体中的位置,若鼠标在窗体的客户区内,则修改鼠标指针,若在非客户区内,还原鼠标指针。具体实现过程如下:
----
第一步:新建工程,并添加一个标准模块。
----
第二步:在标准模块的通用声明过程中,添加如下代码:
Option Explicit Public Declare Function CallWindowProc Lib
"user32" Alias " CallWindowProcA" (ByVal
lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As
Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function SetWindowLong Lib
"user32" Alias "SetWindowLongA" (ByVal
hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As
Long) As Long Public Declare Function LoadCursorFromFile
Lib "user32" Alias "LoadCursorFromFileA"
(ByVal lpFileName As String) As Long Public Declare
Function SetCursor Lib "user32" (ByVal hCursor
As Long) As Long Public Declare Function DefWindowProc Lib
"user32" Alias "DefWindowProcA" (ByVal
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long,
ByVal lParam As Long) As Long Public Const WM_SETCURSOR =
&H20 Public Const GWL_WNDPROC = (-4) Public Const
HTCLIENT = 1 Dim CursorWnd As Long Dim OldProc As Long
----
第三步:在标准模块中添加如下几个过程和函数:
⒈ SetCursorForm过程 Public Sub SetCursorForm(frm As
Form) If OldProc <> 0 Then Exit Sub CursorWnd =
frm.hwnd OldProc = SetWindowLong(frm.hwnd, GWL_WNDPROC,
AddressOf MyWndProc) End Sub ⒉ Release过程 Public Sub
Release() If OldProc = 0 Or CursorWnd = 0 Then Exit Sub
SetWindowLong CursorWnd, GWL_WNDPROC, OldProc OldProc = 0
CursorWnd = 0 End Sub ⒊ 窗口函数MyWndProc Public
Function MyWndProc(ByVal hwnd As Long, ByVal wMsg As Long,
ByVal wParam As Long, ByVal lParam As Long) As Long Select
Case wMsg Case WM_SETCURSOR'截获消息 If wParam
CursorWnd Then'不是自己的窗口 MyWndProc =
DefWindowProc(hwnd, wMsg, wParam, lParam) Exit Function
End If If LOWORD(lParam) <> HTCLIENT Then'不是客户区
MyWndProc = DefWindowProc(hwnd, wMsg, wParam, lParam) Exit
Function End If MyWndProc=SetCursor(LoadCursorFromFile
("D:\Vb\Graphics\Cursors\h_move.cur")) Exit
Function End Select MyWndProc = CallWindowProc(OldProc,
hwnd, wMsg, wParam, lParam) End Function
---- ⒋ LOWORD函数,该函数的作用是从32位整数中提取低字部分。因为鼠标在窗体的不同区域,lParam的低字返回不同的值。
Private Function LOWORD(lValue As Long) As Integer If (lValue
And &HFFFF&) > &H7FFF Then LOWORD = (lValue
And &HFFFF&) - &H10000 Else LOWORD = lValue
And &HFFFF& End If End Function
---- 第四步:双击Form1窗体,在Form_Load过程中加入下列代码:
SetCursorForm Me 在Form_UnLoad过程中加入下列代码:
Release
---- 第五步:保存,按F5或单击启动按钮,测试工程。注意,关闭窗体时,必须用窗体右上角的关闭按钮或窗体系统菜单中的关闭菜单。不能使用Visual
Basic环境提供的结束按钮,否则会出现Windows非法操作的错误。
四、结束语
----
子类处理是一种功能强大的技术,它能极大地方便我们的编程,因为我们不仅能修改控件和窗体的标准行为,而且能对那些通常不以事件形式向VB程序员反馈的消息进行处理。但是,子类处理很难保证其正确性,因为在Visual
Basic环境中,调试子类处理代码是非常困难的事情。因此,建议读者要想完成子类处理任务,最好采用由第三方提供的控件或DLL。
|