CALLBACK 函数

Ø         CALLBACK == WINAPI ==__stdcall

    实际上就是一种调用模式,就是约定由调用函数来自行解决压栈参数, pascal 调用

Ø         #define CALLBACK    __stdcall

    #define WINAPI      __stdcall

    #define WINAPIV     __cdecl

    #define APIENTRY    WINAPI

    #define APIPRIVATE __stdcall

    #define PASCAL     __stdcall

    #define cdecl _cdecl

    #ifndef CDECL

    #define CDECL _cdecl

    #endif

    几乎我们写的每一个WINDOWS API函数都是__stdcall类型的,为什么??

    首先,我们谈一下两者之间的区别:

WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清楚,这里就是问题的关键,如何清除??

如果我们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。

如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。

那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl

到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字

Ø         CALLBACK函数也称为回掉函数,是由你自己来设计,但是供windows调用的,如果你写过SDK程序,就该知道WindowProc函数,它是你自己写的,什么消息怎么处理,但是,在程序中你并没有去调用这个函数,因为不需要你自己调用,到了该调用的时候windows自然会去调用.如果你的函数不是回掉函数,就不要用CALLBACK或者_cdecl来修饰。

如果你写回掉函数,而且它的参数所占的字节是不可变的,那么就用CALLBACK(大部分是这种情况),如果参数可能会变,那么就用_cdecl

Ø         Callback函数是你提供给系统调用的函数。很多情况下,系统某个情况下,定义需要执行某个操作,而操作本身由有用户的程序来提供,这时,就要用到回调函数了。所以,简单地说。回调函数,就是你写一个函数,在系统定义的地点提供给系统调用。

举个例子:SetTimer(),一种处理是,你响应WM_TIMER消息,这暂且不讨论;还有一种用法,就是你提供一个函数,让系统在产生timer消息时自动调用,这种情况下,你可以写好一个timer消息的处理函数,把函数的地址作为SetTimer()的参数,而你这个timer消息的处理函数,就是回调函数。

Ø         其实callback并不仅限于系统调用,用户根据需要,可以建立自己的Callback机制。比如网络通讯,当接收线程(可能专门有一个类封装网络接收行为)收到数据包,需要通知上层(可能又有一个类封装上层数据处理).那么我认为Callback最本质的特征包括两点:注册和触发。实现可以是各种各样的形式,但机制都是如此。比如对于两个类而言,给出以下示例代码:

 

 

#include

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的吧。

我们自己设计的函数往往是用来实现特定功能的函数,为了实现一些功能,我们可能要在函数中调用操作系统提供的服务,例如textout(),我们的自己设计的函数是用来由我们的主程序调用的。这就是一般意义上的函数,如下所示:

void fun1();

main()

{     .......;

          fun1();     .......;

}

void fun1()

{

}

你可以看到,在主程序中一定会有对我写的函数的调用

callback函数与此不同,它不是由我们的主程序来调用的,它是为操作系统准备的,也就是说,我们的主程序中绝对看不到对callback函数的调用,你想想,你肯定没有见过在WinMain()中对winproc的调用。

回调的意思就是反过来调用,因为我们习惯了从我们的程序中调用操作系统的服务,却对操作系统调用我们的函数不习惯,所以我们写的为操作系统调用的函数叫“回调”函数。

这类函数主要是我们为WINDOWS编写的窗口处理函数,

当某个消息到达时,操作系统会调用我们写的回调函数,所以我们看不到主程序中对回调函数的调用。

 刘虎翼:
    编程工具: C++ BUILDER 3.0
    
操作系统: WIN98
    
我想在C++ 中使用回调函数,请问它的内在机制如何,另外怎么定义。我用DialogBox函数时,如何使用回调函数? 它和钩子函数有何不同?多谢指教!!!拜托!!! 

回答:

    使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。至于如何定义回调函数,跟具体使用的API函数有关,一般在帮助中有说明回调函数的参数和返回值等。C++中一般要求在回调函数前加CALLBACK,这主要是说明该函数的调用方式。DialogBox的回调函数实际上是个窗口过程,用来处理所有消息。其定义为:
    BOOL CALLBACK DialogProc(
    
     HWND hwndDlg, // handle of dialog box
     UINT uMsg, // message
     WPARAM wParam, // first message parameter
     LPARAM lParam // second message parameter
     );
    
Win32 API中有详细说明。一般使用C++ BuilderMFC的往往没有使用SDK编程的经验,建议找一些SDK编程的书看一下,否则很难理解如何使用窗口过程。
    
至于钩子函数,只是回调函数的一个特例。习惯上把与SetWindowsHookEx函数一起使用的回调函数称为钩子函数。也有人把利用VirtualQueryEx安装的函数称为钩子函数,不过这种叫法不太流行。
    
     frank
的意见:
    
我对回调函数的理解虽然粗浅,但是我觉得会让人更容易理解:回调函数就相当于一个中断处理函数,由系统在符合你设定的条件时自动调用。为此,你需要做三件事:1,声明;2,定义;3,设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于系统调用。
    
声明和定义时应注意:回调函数由系统调用,所以可以认为它属于WINDOWS系统。不要把它当作你的某个类的成员函数。     
     ping
的意见:
    frank
说:回调函数属于WINDOWS系统。我觉得不应该说回调函数是属于系统的。应该说是程序把这段代码的触发交由系统来做。而这种做法是WINDOWS提供的处理机制吧,因为消息是系统一手掌握着的,由系统来调用我们的程序对消息的处理部分,这样子会比较方便。不然我们又得花力气去读消息列表了。(不知道我说的对不对,接触系统还不深,请高手指教哦)

 

 

 

 

 

posted on 2007-04-13 12:28  dgz  阅读(3657)  评论(0编辑  收藏  举报