VB API 中级入门(二)

此次"中级入门"主要是以消息和子类作讲解,顺带我还会介绍一些其它的知识(纯粹是个人的经历而言),Windows 是一个很丰富的平台,它的包函当然不止这些.在 Windows 编程中,如果你的程序有窗口(Window),那么就一定会和消息打交道!

   

    网友提问时间:"那啥叫消息?"

    举个例子,当你的程序运行时,假设这时你的程序带有一个窗口,那么此时会先创建窗口,这时会激发 WM_CREATE 消息,我上篇文章已经说过,在 Windows 系统中,所以的消息常量都是以 WM_ 为开头的,大家可以打开API 浏览器看看就知道了.再假设如果你用鼠标左键点击所创建的窗口,此时会激发 WM_LBUTTONDOWN 消息,在此我来帮大家分析下这些常量为什么会这么写,首先, WM_ 我们不用去管它,因为后面 LBUTTONDOWN 才是关键,如果你E文OK的话应该可以看得出来,咱们把它进行拆分以后就是这样的:L Button Down ,现在应该明白了吧? L 代表的鼠标左键 Left , Button 自然是按钮的意思, Down 表示鼠标按下的意思,既然这样,那么当鼠标松开时,自然会激发 WM_LBUTTONUP (Left Button Up)消息啦.当然,如果是右键点击的话,那么自然同理,WM_RBUTTONDOWN,折分出来就是: Right Button Down.(俺曰:明白否? 答:所有网友沉默中..)有人可能会问到,为什么要采用这种方式去做常量? 答:因为微软中所有的命名风格都是采用匈牙利命名法,大家可以到网上搜搜"关于匈牙利命名法".

   

    接上面,再再假设如果你的程序要退出了,这时你程序肯定会先关闭窗口,释放相关的资源,然后退出,这时窗口会激发 WM_CLOSE 消息,至于这些个E文我都不用解释了吧?

   

    从上面这些来看,在VB中我们窗口,控件等事件,几乎都是以消息驱动来完成的,所以说,如果你想编写好 Windows 窗口程序,对消息的理解不得忽视.在上一篇API 中级入门中,我给大家讲解了如何使用 SendMessage 等API函数给程序发送消息,如果你在前篇文章不明白为什么那么做,那么我将会在下面为你解答.

   

   

   

   

   

    附注:前段时间有网友发伊妹儿跟我说不太明白 SendMessage 后面两个参数为什么要根据 Msg 而定义,可能前段时间写的不太详细,这里我再仔细的说明下,还是以实例的方法来解答.

   

    附: 怎么分别 SendMessage 后面两个参数应该传什么值.

   

    首先我们来看看 SendMessage 这个API

   

    Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

    很显然,这是一个发送消息的API,从字面上的意思我们都可以看出,如: Send = 发送的意思, Message = 自然是消息的意思了.我们说过 Windows 窗口程序大部分都是以消息进行处理的,窗口创建时系统就向程序发送 WM_CRATE 创建消息,窗口绘画时系统就给程序发送 WM_PAINT 消息,窗口点击时系统就给程序发送 WM_LBUTTONDOWN 消息,窗口关闭时系统就给程序发送 WM_CLOSE,消毁窗口时系统就会给程序发送 WM_DESTROY 消息,所以说,一个窗口程序是离不开消息的.当然,微软为了能让我们更好的控制窗口,给我们提供了很多窗口操作接口(这里指API),而 SendMessage 就是其中的一个,通过该函数可以向任何有窗口的程序发送任何消息,只要是所接收对象的窗口能处理的消息.

   

    现在再来说说后面两个参数传递的具体定义,可以说它们完全根据 wMsg 参数而定,说到这里可能网友有些糊涂了,怎么根据 wMsg 参数而定?该怎么定义?咳~~大家别急哈,我会帮大家慢慢搞明白的.

   

    首先,还是以一个例子作以说明,假设这里所有的API和常量你已声明:

   

    SendMessage Form1.hWnd, WM_CLOSE, 0, ByVal 0&

   

    看看上面一段代码,它所执行的功能为关闭我们的 Form1 窗口.

   

    第一个参数自然是句柄了,你要给哪个窗口发送消息,你就传递哪个窗口的句柄,这很容易明白是吧?

   

    第二个参数我前面讲过,自然是给某个窗口发送的消息,这里是 WM_CLOSE 关闭消息,说白了就是向一个窗口发送关闭窗口消息.

   

    现在来看看第三个参数,这里有些人可能搞不明白为什么就传0?怎么不传一,二,三或四呢?如果你有这个疑问可以尝试着把该参数换成100, 1000, 10000 都行,你看看会不会起什么作用?我们来看看 MSDN 的说法:

   

    wParam : This parameter is not used.

   

    翻译过来就是:这个参数不被使用. 那为啥不被使用呢?很简单,你给一个窗口发送关闭消息,当接收窗口收到该消息时就会作出退出操作,而这时的窗口关闭只需要接收到 WM_CLOSE 消息即可,所以这时你传递任何值它都会被忽略掉不处理,所以说,你就算换在 1,2,3,4,5,6 我想都不会发生任何作用.

   

    那这里可能就人要问了,那既然不用的话这个参数还有存在下去的必要吗?我的回答是:当然有必要!后面我会给大家解开这个困惑的.

   

    再看看最后一个参数,这里也有网友向我询问过,问为什么要 ByVal 0& 这样传递呢?

   

    首先我们看看当前API的最后声明是怎么样的:

   

    lParam As Any

   

    可以看出 lParam 参数是以 ByRef 方法声明的,说白的这时所传递的值是一个地址,如果我们在参数前面加上 ByVal 的话VB就会默认向该参数以值的方式传递,说白了这时所传递的就是一个值.如果你还不明白什么是地址什么是值,可以到网上查查相关资料.

   

    再看看后面为什么要加个 & ,这个符号在VB中如果以运算方式来看的话,它是一个链接符,用于链接两个字符串.如果以数据类型来看的话,它所代表的含意相当于 Long 变量.大家可以看看下面这个API声明方式:

   

    Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

   

    看出什么区别没有?最后一个参数是以 ByVal lParam As Long 方式声明的,所以如果使用以后就会是这样的:

   

    SendMessage Form1.hWnd, WM_CLOSE, 0, 0

   

    现在你应该明白了?那么再看看这个参数为什么也要为0,我们还是先看看 MSDN 的说法:

   

    lParam : This parameter is not used.

   

    还是那句老话:该参数不被使用.所以这里我们默认就以 0 作填充了,因为这个参数不管你传递什么值所接收窗口都不会处理这个值的.所以嘛,忽略,忽略掉!~~

   

    现在,我们再说说这两个参数存在下去的必要性,首先,我们看看原来的 WM_SETTEXT 消息(假设API都已声明):

   

    SendMessage Form1.hWnd, WM_SETTEXT, 0, ByVal "is demo"

   

    前面两个参数我都不说了,来看看第三个参数为何也是零? 下面是MSDN的说法:

   

    wParam : This parameter is not used.

   

    还是那种话:该参数不被使用.所以说咱们在这个参数传递什么值,窗口消息都不会处理的,所以填充0.

   

    再看看最后一个参数,下面是MSDN的说法:

   

    lParam : Pointer to a null-terminated string that is the window text.

   

    翻译过来可能就是: 该参数须传递一个以 NULL 为结尾的字符串指针.(俺E文不好,大大们觉得翻译有误还请多多包涵撒~~~).这里所指的 NULL 我们可以理解为VB中的 Chr(0).

   

    那么这样看来,这个参数不再是零了,而是一个字符串指针.那么看看我们上面是怎么传递的:

   

    ByVal "is demo"

   

    注意:这里参数默认是以 lParam As Any 方式声明的,不是以 ByVal lParam As Long.

   

    看看上面的,如果你声明为 ByVal lParam As Long 整形那么应该怎么传递这个字符串呢(ps: 如果你会指针你当然也有办法?可以用 StrPtr,超出本文范围,这里不作详解)?所以呢,咱们还是要保留该参数的默认声明方式为 Any,那么我们可以传递任何值给这个参数.

   

    现在来看看为什么前面还要加个 ByVal, ByVal 的意思已经说过,是以值方式传递.这里之所有在传递字符串时需要用到 ByVal,那是因为VB中的字符串默认就是一个指针.注意了, VB中的字符串默认就是一个指针!!!而 Windows 操作系统中所有关于字符串的传递都是以指针方式进行的,不可能直接把几个字符串按值传递.所以这里传递的其实是

   

     ByVal 字符串地址

    

     现在,应该很清楚明白了?如果你把 ByVal 去掉的话,那么传递就是:

    

     字符串地址的地址

    

     这又是指向内存哪个地方的东西??明白了吧??不信你可以试试执行后的结果是不是预想的那样.

    

     现在又有人要向我发飙了,问: 看了这么多示例?为什么 wParam 参数老为零?  答:哎呀呀呀~~~,你小子,了不起啊~~

    

     再来看看另一个示例(假设你已声明API):

    

Private Sub Command1_Click()

    SendMessage Me.hwnd, WM_KEYDOWN, 65, ByVal 0&

End Sub

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)

    MsgBox Chr(KeyCode)

End Sub

    至于 Form_KeyDown 这里面的东西我就不说了,基本的东西了.只看 Command1_Click 中的代码,首先 WM_KEYDOWN 是按键消息,你可以拆分看看 Key Down,是不是?该消息说白了就是按键按下时所激发的消息.

    

    最后一个参数我们不去看它,这里只作一个示例,有兴趣的朋友自己查MSDN吧,先来看看MSDN对了WM_KEYDOWN 消息的第三个参数的说明:

   

    wParam : Specifies the virtual-key code of the nonsystem key.

    

    翻译过来就是:指定一个虚拟键码,非系统键.这里所指的当然是那些 A, B, C,D等等按键编码了.OK了,这里不在为0了,得传递点东西上面去了,这里我默认是传递的一个 65,大家可以用 Chr(65) 看看是什么键.然后把这个按键消息发送给我自己,当我收到以后,就会调用 MsgBox 得到传递过来的键值了.

   

    Now,大家应该明白了吧? ^_^

一,初识子类

   

    当你还不碰过子类的时候,你看到这个标题,定会问:"啥叫子类?".因为你知道我定会为你解答.(阴险哪~~~),因为鄙人文才不好,不知如何以最详细最能理解的方式为你解答,所以到网上偷了一段,还请各位笑纳:

   

    子类处理,是一种功能强大的技术,它的作用是对发送到窗口的消息进行处理,我们完全可以用自己定制的一个窗口函数替代它,并保留指向默认窗口函数的指针,当一个消息到达窗口时,自制的窗口函数会拦截它并进行识别处理,对不能识别或不需进行特别处理的消息,就通过指向默认窗口函数的指针传递给默认的窗口函数进行处理,这样便扩充了默认窗口函数的功能。这种用定制的窗口函数代替默认的窗口函数,拦截并处理到达窗口的消息的技术,我们就称之为“子类处理”,定制的函数我们称之为“回调函数”。

   

    上面解释可能较复杂,但是我们现在也没必要完全搞懂它,因为有了实例这一切就自然明白了.

   

    首先,在使用子类的时候,我们需要用到三个API函数,它们分别是 GetWindowLong, SetWindowLong, CallWindowProc(没吓到你吧?),这三个函数不难,可以说是很简单,有了它们,我们实现子类化成为可能,先来看看 GetWindowLong API函数的原型:

   

    Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long

    前面的我都不说了,只看参数:

   

    ByVal hwnd As Long ,不用我说了吧?传句柄的(问:谁的句柄? 答:当然是你要子类的那个窗口的句柄啦)

   

    ByVal nIndex As Long ,咳咳,这个嘛,需要传递一个常量,至于传递什么样的常量,咱们用前段时间学习的API分析大法分析下不就OK了么?(嘿~~聪明)

   

    第二个参数具体传递什么样的参数,根据这个API的名称进行分析(问:为什么啊? 答:倒,API的常量一般都和API名字有很大关联的,难道还分析你的名字不成么?),首先是  Get = G, Window = W, Long = L,合成以后就是 GWL_ ,OK ,打开API 浏览器,转到常量,输入 GWL_ 四个字符看看?有没?没有我马上玩游戏去~~~~~~~~~~

   

    Public Const GWL_EXSTYLE = (-20)

    Public Const GWL_HINSTANCE = (-6)

    Public Const GWL_HWNDPARENT = (-8)

    Public Const GWL_ID = (-12)

    Public Const GWL_STYLE = (-16)

    Public Const GWL_USERDATA = (-21)

    Public Const GWL_WNDPROC = (-4)

    很多哈,但是实现子类的时候我们只需要一个,那就是 GWL_WNDPROC,至于其它的常量我建议大家看看MSDN,如果你看不懂的话可以参考下我以前发的关于一篇大量使用 GetWindowLong 和 SetWindowLong 两个API函数的文章,地址是: http://www.vbgood.com/viewthread.php?tid=46956&highlight= (建议学完该段API介绍再看)

   

    然后,我们再看看 SetWindowLong 原型:

   

    Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

    参数:

   

    ByVal hwnd As Long ,句柄,还是句柄

   

    ByVal nIndex As Long ,好奇的你可能已经发现了这个参数和 GetWindowLong 的第二个参数一样的耶?那么恭喜你,是一样的,包括传递的参数都是一样的,如果你不服气你去试下有没有 SWL_ 开头的常量.^_^

   

    ByVal dwNewLong As Long ,这个嘛~~, 参数的名称已经说明这是一个 New(新的)Long(长整形),当然这里的长整形是指一个地址 Address , you know ?

   

    前段时间我们知道了 Get 与 Set 的区别,那就是一个获取,一个设置,当然这个也不例外.再来看看 CallWindowProc 这个API的原型:

   

    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

    参数:

   

    ByVal lpPrevWndFunc As Long ,这个,我先试着拆分看看意思, Prev 的意思可能是 Previous(原始的), Wnd 自然是 Window(窗口) 意思咯,最后一个 Func 应该是 Function(函数),看看前面的 lp ,如果你会匈牙利命名法的话可以很明显看出来,该参数应该传一个指针,当然VB默认不支持指针,所有的指针类型都是以Long代替(注:貌似在32位系统中指针本来就是一个四字节的整形变量).那么这个参数的意思就是需要传递一个原始窗口指针,至于为什么,在这会可能说不清楚,咱们接着往下看.

   

    ByVal hWnd As Long , 啥也不说了,句柄呗.

   

    ByVal Msg As Long, 消息撒.至于传什么,那就接着往下看

   

    ByVal wParam As Long , 还是那句老话,根据 Msg 消息来定,前篇API教程中我们说过 SendMessage 后面二个参数的具体定义,本次这些也一样

   

    ByVal lParam As Long , 同 wParam 解释一样

   

    这个API的用途就是把已处理的消息按 lpPrevWndFunc(原窗口消息地址) 返回给该地址.

   

    好了,这段算是介绍完了,可能你还处于云山雾里,不过别担心,我能让你吃亏吗?哈哈哈~~~~~

       

   

    二,回调函数

   

    在调用过程时指定的自定义函数被称为回调函数。回调函数(通常简称为“回调”)能够对过程提供的数据执行指定的操作。回调函数的参数集必须具有规定的形式,这是由使用回调函数的 API 决定的。关于需要什么参数,如何调用它们,请参阅 API 文档。

    从上面一段文字描述中我们可以看出,回调函数说白了就是一段自定义的过程函数,至于是什么样的自定义函数,那都是由API来决定的.那么有人会问:咱们这个实现子类化的回调函数是怎么定义的呢?问的好,当然,我还是那句老话,如果你有MSDN的话,建议看看.这里我们实现的子类的回调函数是下面这种样子:

   

    Function WindowProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

   

    怎么样?(网友: 好复杂啊 答:不会吧?这都复杂?那我介绍一个容易记住的方法,把 SendMessage API中的几个参数拿过来用就行了,是不是发现它们都是一样的?然后把最后一个参数改成 Byval ???? Long 就行了),可见这个回调函数和 SendMessage 好像啊,几乎差不多,不过要注意的是: SendMessage 最后一个参数是 lParam As Any,当然 SendMessage 的最后一个参数也可以改成 Byval lParam As Long,当然这里是根据你的需要去定的.

   

    OK,该了解了我们都了解的差不多了?现在该是我们实际操作的时候了,打开VB6,新建标准EXE,然后新建一个标准模块.

   

    注意哦:用VB进行子类是危险的,你要时刻记得保存你的VB文档,否则你写了半天的代码会因为突然崩溃而没保存,到那时就别怪我没提醒你哦.哈哈~~~~~~

   

    提示:在VB中进行子类时,默认只能在标准模块中进行.

三,实际操作的机会来了

   

    在 Form1 中的代码:

   

Private Sub Form_Load()

    pWndProc = GetWindowLong(Me.hwnd, GWL_WNDPROC)

    SetWindowLong Me.hwnd, GWL_WNDPROC, AddressOf WindowProc

End Sub

Private Sub Form_Unload(Cancel As Integer)

    SetWindowLong Me.hwnd, GWL_WNDPROC, pWndProc

End Sub

    在 Module1 中的代码:

   

Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex 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 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 Const GWL_WNDPROC = (-4)

Public pWndProc As Long

Public Function WindowProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

   

    Debug.Print Hex$(Msg)

   

    WindowProc = CallWindowProc(pWndProc, hwnd, Msg, wParam, lParam)

End Function

    就这么一点代码就能实现VB中的子类,可以说是比较简单的,先来看看 Module1 中的代码,至于API函数和常量的声明我就不说了.

   

    首先我们声明了一个 Public pWndProc As Long 变量,这里主要是把它当然指针来使用,这里我们再看看 Form1 中的代码:

   

    pWndProc = GetWindowLong(Me.hwnd, GWL_WNDPROC)  ,可见窗口初始时先获取当前窗口的消息地址,然后再使用 SetWindowLong Me.hwnd, GWL_WNDPROC, AddressOf WindowProc 把地址指向模块中的 WindowProc 回调函数,我们在分析 SetWindowLong, 前面两个参数我就不说了,后面一个用到了 Addressof 函数,这个函数的功能就是返回一个函数的地址.再看看 SetWindowLong 最后一个参数, dwNewLong 肯定与一个新的参数有关,而这里正是指向我们参数自定义的函数地址.

   

    我们不可能 SetWindowLong Me.hwnd, GWL_WNDPROC, WindowProc 这样传递是吧?这样直接把函数当做一个参数是不对的,而第三个参数正好是一新地址,所以我们通过 AddressOf WindowProc 该回调函数地址设置成当前窗口的消息地址,这样我们就能通过 WindowProc 回调函数处理我们的消息了.

   

    接着看 WindowProc 回调函数里的代码:

   

    Debug.Print Hex$(Msg) 这句意思是以十六进制的方式查看当前的消息情况.当然你可以运行该代码,然后你将鼠标移动到Form1窗体上,这时在立即窗口就会显示相应的消息数据.

   

    WindowProc = CallWindowProc(pWndProc, hwnd, Msg, wParam, lParam) 这句的意思是把不需要处理的消息返回给系统,前面我们分析过 CallWindowProc 函数,后面四个参数我就不多说了,它们都是按照回调函数中的参数原样返回就行了,主要看第一个参数. CallWindowProc 的第一个参数上面分析时说明为一个地址,这个地址必须是原有的.通过 Form_Load 中的代码我们就可以看出,先是通过 GetWindowLong 获取Form1窗口的默认消息地址,然后再通过 SetWindowLong 把消息的流通地址转到我们的回调函数中,然后通过回调函数处理,当不需要时需要再通过 CallWindowProc 返回给窗体默认的消息地址就行了.

   

    再看 Form_Unload 中的代码,这是一个还原,主要是当我们退出时不在处理窗体默认的消息时应该返回给系统,这里是需要注意的,否则程序可能会出现异常.

   

    说了这么多,可能有些人还不太怎么明白,不过不要紧,以后多多接触这方面的例子就自然会明白了.好了,我来拟个运行顺序,希望大家能够明白它们的运行机制.

   

    首先,窗体加载时,我们使用

   

    pWndProc = GetWindowLong(Me.hwnd, GWL_WNDPROC)

   

    保存当前窗体的默认消息地址, 然后再通过

   

    SetWindowLong Me.hwnd, GWL_WNDPROC, AddressOf WindowProc

   

    把当前窗体的默认消息地址设置到我们的回调函数地址处,然后我们就可以通过回调消息来控制当前窗体的消息了

   

    WindowProc = CallWindowProc(pWndProc, hwnd, Msg, wParam, lParam)

   

    当消息不用时,我们就要把这个消息返还给系统.该参数只按原路照写就行了.这里切记,如果少了这个函数,程序接不到相应的消息会死掉的

   

    SetWindowLong Me.hwnd, GWL_WNDPROC, pWndProc

   

    最后不用时咱们就把地址返还给系统.当然有时缺少这一句不会出现什么问题,但是以良好的SDK编程规则来说,最好把这句写上.

   

   

    OK,这段似乎较复杂,不过不要紧,先苦后甜嘛,等你熟练使用子类的时候,你的Windows 编程功力就更上一层楼.

   

   

    休闲时间广告: 您曾经是否为无法处理窗口的消息而烦恼?您曾经是否看到别人漂亮的自绘菜单而羡慕?您曾经是否因为自己的窗口功能太单一而忧郁?现在好了,赶快学习VB子类吧,有了它,一切都会好起来的.赶快拿起电话定购吧: 电话:110

   

   

    上面广告纯属虚构,如有雷同,算我倒霉.其实子类的好处不止这些,当然也有很多很多,下面我会举一些例子来说明子类的好处.

   

   

    四,理论,实操一起抓

   

    1,让你发消息关不掉我

   

    首先新建标准EXE,然后新建一个标准模块:

   

    在 Form1 中加入以下代码

   

Private Sub Form_Load()

    pWndProc = GetWindowLong(Me.hwnd, GWL_WNDPROC)

    SetWindowLong Me.hwnd, GWL_WNDPROC, AddressOf WindowProc

End Sub

Private Sub Form_Unload(Cancel As Integer)

    SetWindowLong Me.hwnd, GWL_WNDPROC, pWndProc

End Sub

    然后再在 Module1 中加入以下代码:

   

Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex 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 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 Const GWL_WNDPROC = (-4)

Public Const WM_CLOSE = &H10

Public pWndProc As Long

Public Function WindowProc(ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

    If Msg = WM_CLOSE Then '处理 WM_CLOSE 窗口关闭消息

   

        WindowProc = 1

        Exit Function

       

    End If

   

    WindowProc = CallWindowProc(pWndProc, hwnd, Msg, wParam, lParam)

End Function

    其它代码我就不说了,主要是看回调函数中的那几段处理代码. 首先通过 Msg 参数判断当前激发的消息,先前说过,Windows 的所以消息都是以 WM_ 开头的,大家可以找找看.如果是我们要拦截的消息时,那么用下面代码

   

    WindowProc = 1

    Exit Function

   

    两句搞定, WindowProc = 1 返回为 True,意思是该消息处理完成,然后 Exit Function 自然是退出该函数,这句代码的意思说白了就是不让执行下面的 CallWindowProc,如果你给CallWindowProc执行了,消息也就自然返回给程序了,当程序接到WM_CLOSE,自然就会退出了.所以说白了,子类拦截消息就是不让执行 CallWindowProc.应该很简单吧??

   

    好了,运行起来试试(别忘了保存)?如果你的VB崩溃了,说明你的代码有误,请仔细检查下.找一个利用 SendMessage 发送WM_CLOSE消息关闭窗口程序试试,发现无法正常关闭你的窗口吧?(注:别拿 Windows 任务管理器试,因为它不单单只是 SendMessage. 如果你实在拿不出什么工具来试,可以自己写个 SendMessage me.hWnd, WM_CLOSE, 0, 0 试试)

posted @ 2010-09-27 21:09  年轻的蜗牛  阅读(505)  评论(0编辑  收藏  举报