CallBack and Hooks

在最近的项目(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()

### UVM中Callback回调函数的使用方法 #### 1. 声明一个UVM callback空壳类 为了支持回调机制,首先需要声明一个通用的`uvm_callback`派生类作为模板。这个类通常是一个空壳类,仅继承自`uvm_callback`[^3]。 ```systemverilog class my_callback extends uvm_callback; endclass : my_callback ``` #### 2. 扩展具体的回调类 接下来,从上述空壳类进一步扩展出实际使用的具体回调类,并在其中实现所需的逻辑操作。这些逻辑可以是在某个事件发生前后的特定处理。 ```systemverilog class pre_post_trans_callback extends my_callback; function void post_trans(my_transaction tr); $display("Transaction modified by callback: %s", tr.convert2string()); // 修改事务数据或其他操作 tr.data += 1; // 示例:修改交易的数据字段 endfunction : post_trans endclass : pre_post_trans_callback ``` #### 3. 绑定回调类与目标组件 通过`uvm_register_cb()`宏将回调类注册到指定的目标组件上。这样做的目的是让该组件能够识别并管理已注册的回调实例[^2]。 ```systemverilog initial begin uvm_register_cb(sequencer_type, pre_post_trans_callback); // 注册回调至序列器类型 end ``` #### 4. 插入回调调用位置 利用`uvm_do_callbacks()`宏来触发已经绑定好的回调函数执行。此过程发生在预设的关键时刻点附近,比如某次传输完成之后等场景下。 ```systemverilog task sequencer::execute_item(item); ... uvm_do_callbacks(this, pre_post_trans_callback, cb.post_trans(item)); ... endtask : execute_item ``` #### 5. 添加具体的回调实例 最后一步是创建具体的回调对象并将它们加入到运行时列表当中去以便于后续被调用到。 ```systemverilog pre_post_trans_callback cbi = new(); void'(uvm_callbacks#(my_component)::add(obj, cbi)); // obj为目标组件实例 ``` 以上步骤展示了如何在一个典型的UVM环境下设置和运用回调机制的过程。 ### 完整示例代码 下面提供了一个完整的例子展示整个流程: ```systemverilog // Step 1 & 2: Define the base and specific callbacks class my_base_callback extends uvm_callback; endclass class modify_data_callback extends my_base_callback; function void do_something(input int data); `uvm_info("CALLBACK", $sformatf("Data before modification=%d",data), UVM_LOW) data++; `uvm_info("CALLBACK", $sformatf("Data after modification=%d",data), UVM_LOW) endfunction endclass // Component where we want to use this callback mechanism. class comp_with_hooks extends uvm_component; ... task run_phase(uvm_phase phase); fork forever begin bit[7:0] data_to_process; @(posedge clk iff (reset === 'b0)); // Simulate some processing logic here... data_to_process = get_next_data(); // Inserting our hook point using macro provided by UVM framework itself. uvm_do_obj_callbacks(comp_with_hooks,modify_data_callback,data_to_process); process_the_data(data_to_process); end join_none endtask endclass ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值