CallBack and Hooks

    技术2022-05-11  138

    在最近的项目(VB)中,需要处理列表滚动条滚动事件,VB中并没有列表滚动条事件,因此用回调函数处理该事件

     

     

    使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。

     

     

    回调函数就相当于一个中断处理函数,由系统在符合你设定的条件时自动调用。为此,你需要做三件事:

    1,声明;

    2,定义 定义回调函数,跟具体使用的API函数有关,一般在帮助中有说明回调函数的参数和返回值等;

    3,设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于系统调用。

        声明和定义时应注意:回调函数由系统调用,应该放在公共模块里,不能把它当作某个类的成员函数。

        钩子函数是回调函数的一个特例。习惯上把与SetWindowsHookEx函数一起使用的回调函数称为钩子函数。

     

     

    在本例中,用回调函数处理list的滚动事件

    所用的API函数有

    '为指定的窗口设置信息

    Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (

        ByVal hwnd As Long,  _    '取得信息的窗口的句柄

        ByVal nIndex As Long, _   '

        ByVal dwNewLong As Long_  'nIndex指定的窗口信息的新值

        ) As Long

     

     

      nIndex欲取回的信息,可以是下述任何一个常数:

      Const GWL_EXSTYLE = (-20)               '扩展窗口样式

      Const GWL_STYLE = (-16)           '窗口样式

      Const GWL_WNDPROC = (-4)        '该窗口的窗口函数的地址

      Const GWL_HINSTANCE = (-6)       '拥有窗口的实例的句柄

      Const GWL_HWNDPARENT = (-8)         '该窗口之父的句柄。

    '不要用SetWindowWord来改变这个值

      Const GWL_ID = (-12)               '对话框中一个子窗口的标识符

      Const GWL_USERDATA = (-21)       '含义由应用程序规定

      Const DWL_DLGPROC =  4                '这个窗口的对话框函数地址

      Const DWL_MSGRESULT = 0               '在对话框函数中处理的一条消息返回的值

      Const DWL_USER = 8                      '含义由应用程序规定

     

     

    '将消息信息传送给指定的窗口过程

    '返回值指定了消息处理结果,它与发送的消息有关

    Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _

       (ByVal lpPrevWndFunc As Long, _          '指向前一个窗口过程的指针

        ByVal hwnd As Long, _                  '指向接收消息的窗口过程的句柄

        ByVal Msg As Long, _                   '指定消息类型

    ByVal wParam As Long, _                '指定其余的、消息特定的信息。

    '该参数的内容与Msg参数值有关

    ByVal lParam As Long _            '指定其余的、消息特定的信息。

    '该参数的内容与Msg参数值有关

        ) As Long

    lpPrevWndFunc指向前一个窗口过程的指针如果该值是通过调用GetWindowLong函数,并将该函数中的nlndex参数设为GWL_WNDPROCDWL_DLGPROC而得到的,那么它实际上要么是窗口或者对话框的地址,要么就是代表该地址的句柄。

     

     

     

     

     

    首先定义一全局变量,用来存放回调函数的返回地址

    Public g_OldProc As Long

    Const GWL_WNDPROC = (-4)

    '自定义的消息处理函数 ScrollProc, 该函数必须放在公共模块中

    '处理list滚动事件

    Public Function ScrollProc(ByVal hWnd As Long, ByVal iMsg As Long, _

        ByVal wParam As Long, ByVal lParam As Long) As Long

       

        Const WM_HSCROLL = &H114

        Const SB_ENDSCROLL = 8

       

        '获取消息

        If (iMsg = WM_HSCROLL) Then

            If SB_ENDSCROLL = Val("&H" & Right(Hex(wParam), 4)) Then

                    'TODO

                    'Add your code here

            End If

        End If

        '设置回调函数

        ScrollProc = CallWindowProc(g_OldProc, hWnd, iMsg, wParam, lParam)

      End Function

     

     

    '在程序开始运行时将列表控件的消息处理函数设置为自定义的消息处理函数 ScrollProc

    g_OldProc = SetWindowLong(dg_Result.hWnd, GWL_WNDPROC, AddressOf ScrollProc)

     

     

    '程序退出时还原列表控件消息处理

    '将列表控件的消息处理函数还原

    Call SetWindowLong(dg_Result.hWnd, GWL_WNDPROC, g_OldProc)

     

    Note: 不能用传址方式来调用API,如果用传址方式的话那么传递的是指向指针的指针,API将不能返回数据,并且造成访问数据出错,所以需要用ByVal传递字符串指针

     

    钩子的基本原理

    钩子的基本原理

    钩子的本质是一段用以处理系统消息的程序,通过系统调用,将其挂入到系统。钩子的种类有很多,每一种钩子负责截获并处理相应的消息。钩子机制允许应用程序截获并处理发往指定窗口的消息或特定事件,其监视的窗口即可以是本进程内的也可以是由其他进程所创建的。在特定的消息发出,并在到达目的窗口之前,钩子程序先行截获此消息并得到对其的控制权。此时在钩子函数中就可以对截获的消息进行各种修改处理,甚至强行终止该消息的继续传递。

     

     

    任何一个钩子都由系统来维护一个指针列表(钩子链表),其指针指向钩子的各个处理函数。最近安装的钩子放在链的开始,最早安装的钩子则放在最后,当钩子监视的消息出现时,操作系统调用链表开始处的第一个钩子处理函数进行处理,也就是说最后加入的钩子优先获得控制权。在这里提到的钩子处理函数必须是一个回调函数(callback function),而且不能定义为类成员函数,必须定义为普通的C函数。在使用钩子时可以根据其监视范围的不同将其分为全局钩子和线程钩子两大类,其中线程钩子只能监视某个线程,而全局钩子则可对在当前系统下运行的所有线程进行监视。显然,线程钩子可以看作是全局钩子的一个子集,全局钩子虽然功能强大但同时实现起来也比较烦琐:其钩子函数的实现必须封装在动态链接库中才可以使用。

     

     

    操作系统是通过调用钩子链表开始处的第一个钩子处理函数而进行消息拦截处理的。因此,为了设置钩子,只需将回调函数放置于链首即可,操作系统会使其首先被调用。在具体实现时由函数SetWindowsHookEx()负责将回调函数放置于钩子链表的开始位置。

     

     

    SetWindowsHookEx()函数完成对钩子的安装后,如果被监视的事件发生,系统马上会调用位于相应钩子链表开始处的钩子处理函数进行处理,每一个钩子处理函数在进行相应的处理时都要考虑是否需要把事件传递给下一个钩子处理函数。如果要传递,就通过函数CallNestHookEx()来解决。尽管如此,在实际使用时还是强烈推荐无论是否需要事件传递而都在过程的最后调用一次CallNextHookEx( )函数,否则将会引起一些无法预知的系统行为或是系统锁定。该函数将返回位于钩子链表中的下一个钩子处理过程的地址,至于具体的返回值类型则要视所设置的钩子类型而定。

    最后,由于安装钩子对系统的性能有一定的影响,所以在钩子使用完毕后应及时将其卸载以释放其所占资源。释放钩子的函数为UnhookWindowsHookEx()

     

     

     

     

     

     


    最新回复(0)