1.回调函数与普通函数的区别
从概念上讲,回调函数与普通函数的本质在于:调用者的不同。
普通函数由程序员代码调用,而回调函数由操作系统在适当的时间调用。
回调函数主要用于处各种事件和处理。
由于WINDOWS系统中存在大量程序员事先不可知的事件,
例如鼠标的单击,程序员事先无法得知终端用户何时会发出此动作,因此只能:
A.定义事件的处理逻辑,与普通函数的编程一样;
B.告之操作系统自己的处理逻辑,即通知操作系统函数指针;
VC/VB等现代编程语言通过事件编程机制隐藏了这一步
C.操作系统在事件出现时,调用指定的函数(回调函数的概念)处理,这一步完全由系统负责。
回调函数在各种操作系统中普遍存在,是现代操作系统为程序员提供处理异步事件的基本机制之一,在不同的系统中的具体实现方式各不相同;请参阅随机文档。
Callback 函数实质就是你实现这个函数,由操作系统调用。而一般的情况下是,操作系统提供函数由你来调用的。
2.回调函数实际上就起到了消息循环的作用
因为在sdk中只有通过回调函数来发送各自的处理消息
3.C/C++实现
象C/C++这样支持函数指针的语言都有回调函数的概念
它实际上是向被调用函数传一个你的函数地址,然后被调用函数向通过你传入的函数地址来调用你的函数。
比如你做了一个遍历树的函数,但你不知遍历者将对各节点做何种处理时,你就可以在这个遍历函数中加一个函数地址的参数,这样调用者在遍历该树时就可以做各种有意义的工作了:比如打印各节点数据、汇总所有节点之类。
4.Windows回调函数
回调函数是用来处理窗口消息的函数,一般类型为
WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
hWnd为窗口句柄,message为消息ID,后面两个为消息参数。
MFC将一部分处理消息的函数封状在CWnd类中,如OnCreate等,其参数也从WPARAM wParam, LPARAM lpram转换为LPCREATESTRUCT结构(可以查看映射宏定义及MFC源代码)
而其他的有些也可以用回调函数,如WM_TIMER消息,可以在SetTimer函数里面第三个参数指定回调函数,若为NULL则应该在OnTimer函数中处理该消息。
5.MSDN中的描述
Used to asynchronously read the messages in a queue.
It is an application-defined function that MSMQ calls
when a message is available, a time-out occurs, or an error occurs.
6.Callback最本质的特征包括两点:注册和触发
Callback函数是你提供给系统调用的函数。
很多情况下,系统某个情况下,定义需要执行某个操作,而操作本身由有用户的程序来提供,这时,就要用到回调函数了。
所以,简单地说。回调函数,就是你写一个函数,在系统定义的地点提供给系统调用。
举个例子:SetTimer()
一种处理是,你响应WM_TIMER消息,这暂且不讨论;
还有一种用法,就是你提供一个函数,让系统在产生timer消息时自动调用,这种情况下,你可以写好一个timer消息的处理函数,把函数的地址作为SetTimer()的参数,而你这个timer消息的处理函数,就是回调函数。
其实callback并不仅限于系统调用,用户根据需要,可以建立自己的Callback机制。
比如网络通讯,当接收线程(可能专门有一个类封装网络接收行为)收到数据包,需要通知上层(可能又有一个类封装上层数据处理).
Callback最本质的特征包括两点:注册和触发。
实现可以是各种各样的形式,但机制都是如此。
比如对于两个类而言,给出以下示例代码:
#include <iostream.h>
class B
{
public:
B();
void OnGetMsg(unsigned long ID,const char * MsgName);
private:
unsigned long m_ID;
};
B::B()
{
m_ID = 1002;
}
void B::OnGetMsg(unsigned long ID, const char *MsgName)
{
cout << "srcObjID = " << ID << ", " << "tgtObjID = " << m_ID <<", \
" <<"Message: " << MsgName << endl;
}
class A
{
public:
A();
void RegisterMsg(B* pb);
void SendMsg(char* msg);
private:
B * m_pb;
unsigned long m_ID;
};
A::A()
{
m_ID = 1001;
m_pb = NULL;
}
void A::RegisterMsg(B* pb)
{
m_pb = pb;
}
void A::SendMsg(char *msg)
{
if(m_pb != NULL)
m_pb->OnGetMsg(m_ID,msg);
}
void main()
{
//产生回调的类对象a
A a;
//相应回调的类对象b
B b;
//A类对象注册
a.RegisterMsg(&b);
//A类对象触发、B类对象响应
a.SendMsg("i'm callback function");
}
|
Callback函数有点类似虚函数,不仅仅系统调用,而且你自己也可以定义Callback函数
比如在自己的类中定义Callback函数的原型,然后在类的其他成员函数中就可以直接调用该Callback函数,而不用管他的具体实现
当然你可以传入参数。而具体实现可能在其他应用程序中或者Dll中,这样可以把接口和实现分离。
reference:SDK(相关概念:API、动态链接库、导入库)其实很简单,SDK 就是 Software Development Kit 的缩写,中文意思就是“软件开发工具包”。
这是一个覆盖面相当广泛的名词,可以这么说:辅助开发某一类软件的相关文档、范例和工具的集合都可以叫做“SDK”。
具体到我们这个系列教程,我们后面只讨论广义 SDK 的一个子集——即开发 Windows 平台下的应用程序所使用的 SDK。
为了解释什么是 SDK 我们不得不引入 API、动态链接库、导入库等等概念。
API也就是 Application Programming Interface
其实就是操作系统留给应用程序的一个调用接口,应用程序通过调用操作系统的 API 而使操作系统去执行应用程序的命令(动作)。
其实早在 DOS 时代就有 API 的概念,只不过那个时候的 API 是以中断调用的形式(INT 21h)提供的
在DOS下跑的应用程序都直接或间接的通过中断调用来使用操作系统功能,比如将 AH 置为 30h 后调用 INT 21h 就可以得到 DOS 操作系统的版本号。
而在 Windows 中,系统 API 是以函数调用的方式提供的。
同样是取得操作系统的版本号,在 Windows 中你所要做的就是调用 GetVersionEx() 函数。
可以这么说,DOS API 是“Thinking in 汇编语言”的,而 Windows API 则是“Thinking in 高级语言”的。
DOS API 是系统程序的一部分,他们与系统一同被载入内存并且可以通过中断矢量表找到他们的入口
那么 Windows API 呢?要说明白这个问题就不得不引入我们下面要介绍得这个概念——DLL。
DLL:Dynamic Link Library(动态链接库)。
我们经常会看到一些 .dll 格式的文件,这些文件就是动态链接库文件,其实也是一种可执行文件格式。
跟 .exe 文件不同的是,.dll 文件不能直接执行,他们通常由 .exe 在执行时装入,内含有一些资源以及可执行代码等。
其实 Windows 的三大模块就是以 DLL 的形式提供的(Kernel32.dll,User32.dll,GDI32.dll),里面就含有了 API 函数的执行代码。
为了使用 DLL 中的 API 函数,我们必须要有 API 函数的声明(.H)和其导入库(.LIB)
函数的原型声明不难理解,那么导入库又是做什么用的呢?我们暂时先这样理解:导入库是为了在 DLL 中找到 API 的入口点而使用的。
所以,为了使用 API 函数,我们就要有跟 API 所对应的 .H 和 .LIB 文件
而 SDK 正是提供了一整套开发 Windows 应用程序所需的相关文件、范例和工具的“工具包”。到此为止,我们才真正的解释清楚了 SDK 的含义。
由于 SDK 包含了使用 API 的必需资料,所以人们也常把仅使用 API 来编写 Windows 应用程序的开发方式叫做“SDK 编程”。
而 API 和 SDK 是开发 Windows 应用程序所必需的东西,所以其它编程框架和类库都是建立在它们之上的
比如 VCL 和 MFC,虽然他们比起“SDK 编程”来有着更高的抽象度,但这丝毫不妨碍它们在需要的时候随时直接调用API函数
阅读(1271) | 评论(0) | 转发(0) |