16.VC(custom)-回调函数(callback function)

来源:互联网 发布:懒惰的开发者 知乎 编辑:程序博客网 时间:2024/05/23 00:11

1.回调函数(callback function)?

回调,就是两个程序A和C,程序A中含有函数b,程序C中含有函数d,当A要调用C的某个功能时,C需要反过来调用A中的函数b,而且是A不得不为C提供函数b,而C不知道A提供的b叫什么,有什么参数,所以C会规定b有什么参数,然后由A通过C中的函数d传入函数b的名字(也就函数d的一个参数和b的函数原型一致),这时b就是回调函数,而d就是注册函数.

举个例子

HHOOK SetWindowsHookEx(          int idHook,    HOOKPROC lpfn,    HINSTANCE hMod,    DWORD dwThreadId);

中参数HOOKPROC 就是相当于上述函数d的一个参数,它指定了回调函数的函数原型,比如在idHook为WH_MOUSE时,查看MSDN,可知"Installs a hook procedure that monitors mouse messages. For more information, see theMouseProc hook procedure",所以MouseProc 就是它指定的回调函数的函数原型(HOOKPROC),继续查看MSDN,可知

LRESULT CALLBACK MouseProc(          int nCode,    WPARAM wParam,    LPARAM lParam);

也就是对于拦截鼠标消息,程序A提供的回调函数b必须像MouseProc有一样的参数,同时通过注册函数SetWindowsHookEx传入b的名称,以告诉windows系统(也就是程序C)可以在得到鼠标消息时,反过来调用函数b进行处理.一般来说,A不会自己调用函数b,A提供函数b就是为了让C来调用的,而且是不得不提供.C提供函数d来规定函数b的原型,并传入它的名称.

 下面举个通俗的例子:

  我们不认识,但我知道你能帮我解决某个难题,所以我打电话向你请教问题,你一时想不出解决方法,我又不能拿着电话在那里一直等,于是我们约定:等你想出办法后再打电话给我,所以,我把电话号码告诉你,我就挂掉电话办其它事情去了。过了XX分钟,我的电话响了,你通过我提供的电话号码打回来告诉我应该如此这般处理。故事到此结束。

 这个例子说明了“异步+回调”的编程模式。(我打电话向你请教问题,我有你的电话,也就是能联系到你的函数),(你一时想不出解决方法,异步操作),(所以,我把电话号码告诉你,注册回调函数),(你通过我提供的电话号码打回来告诉我应该如此这般处理,我的电话号码应该和我告诉你的一致,这是回调函数必须符合接口规范),其中,你后来打电话告诉我结果便是一个“回调”过程。

2.什么时候使用回调?

一般来说,回调是一种嵌入,必须由调用者提供,不同的调用者可以提供不同的回调函数(只要参数符合接口规范)。

3.回调函数不能声明为成员函数?

#define CALLBACK    __stdcall

而在vc++中,所有类的成员函数在定义的时候都被隐式(implicit)定义为__thiscall参数传递方式,那么我们就来研究下__stdcall和__thiscall的区别

msdn对__stdcall的解释:

Microsoft Specific

The__stdcall calling convention is used to call Win32 API functions. The callee cleans the stack, so the compiler makesvararg functions __cdecl. Functions that use this calling convention require a function prototype

msdn对__thiscall的解释:

所以类成员函数不能作为回调函数的主要原因在于类成员函数使用__thiscal参数传递方式,因此需要调用者(caller)通过ecx寄存器提供类对象的指针。而回调函数使用__stdcall参数传递方式,不具备这个特点,但是可以把回调函数声明为静态成员函数.


The __thiscall calling convention is used on member functions and is the default calling convention used by C++ member functions that do not use variable arguments. Under__thiscall, the callee cleans the stack, which is impossible forvararg functions. Arguments are pushed on the stack from right to left, with thethis pointer being passed via register ECX, and not on the stack, on the x86 architecture.

关键句:with the this pointer being passed via register ECX, and not on the stack, on the x86 architecture

成员函数所在类的this指针被存入ecx寄存器(这个特性只针对Intel x86架构)。