现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

谁在调用那些回调函数

2014-07-01 06:25 工业·编程 ⁄ 共 1214字 ⁄ 字号 评论 1 条

在Windows应用程序开发中,有很多地方使用了回调函数。一般的开发并不关心谁来调用这些函数,但如果开发复杂的多线程协作处理程序,或者研究操作系统中程序的“操作权限”问题,您就需要知道您写的这段代码是由谁来调用的,在哪个线程或进程中执行的。

最常见的就是Windows消息响应函数。一般的书本并不讨论这些函数是如何被调用的,只是说消息产生时,这些函数将会执行。初学者也许会以为,如果两个消息一起发出,那么它们的响应函数会同时执行。事实并不是这样。这些函数是串行执行的,也就是说只有执行完一个之后,另一个才有机会被调用。不仅如此,这些函数也不是由什么系统调用的,而是由一个确定的线程中统一管理,这个线程正是你的程序自己的主线程。

您可能自己并没有写这样的代码,但您使用的基础库中有。无论是MFC还是ATL,您都可以在它们的源代码中找到类似下面这样的内容

MSG msg;

while(GetMessage(&msg, NULL, 0, 0))

    {      

      TranslateMessage(&msg);

      DispatchMessage(&msg);

    }

这个是消息循环,也称消息泵。它像个抽水机似的,把消息从一个地方抽出来,然后洒给各个消息响应函数处理。因此也可以说,消息响应函数是从线程的消息泵开始调用的。

还有一种作为参数传进去的回调函数。这些回调函数类似于Java中的那些接口,当调用某个函数时,把它当成参数传进去。在适当的时候,这些被函数会被调用。很多窗口枚举函数就是这样的,比如:EnumChildWindows、EnumDesktopWindows、EnumWindows等。这些函数其实并不是异步函数,在函数返回时,相应的功能已经完成,被传进去的回调函数也已经被调用过了。这种情况下,回调函数可看作是被相应的功能函数调用的。

钩子(HOOK)处理函数也是一种回调函数。我曾一度以为它是由操作系统的某个线程调用的,但事实并非如此。我在钩子函数里使用GetCurrentThreadId测试了一下,发现调用钩子函数的线程竟然是安装钩子的那个线程。那么这个调用是从哪发起的呢?原来还是消息泵!当调用GetMessage或PeekMessage函数时,并不只是把消息从队列中取出来这么简单,而是做了更复杂的工作。具体这些API是如何实现的我们不得而知,但有一个我进行了验证:不启动消息泵,钩子啥也钩不到。所以归根结底,钩子处理函数还是由线程的消息泵开始调用的

Windows使用消息和回调函数来调度程序的执行,同一线程设置的回调函数,无论是消息响应还是钩子,最后仍在这一线程内执行,消息泵负责调度。

了解这一点,我们就可以在这些回调函数中放心地使用任何资源了,不用担心互斥、死锁。但是要担心另外一些问题:比如消息响应的嵌套冲突导致的数据变化、消息的循环发送等。一般这些问题都易于重现,所以还算容易发现和处理。

作者:苏林

目前有 1 条留言    访客:0 条, 博主:0 条 ,引用: 1 条

    外部的引用: 1 条

    • 子线程设置的钩子(HOOK)为什么钩不到消息? | 求索阁

    给我留言

    留言无头像?