回调函数在构建和DLL时如何有用

时间:2010-09-01 07:53:05

标签: c# c++ windows visual-c++ function-pointers

回调函数是否与C#(。NET)中的事件等效。

我对回调函数的理解是,它是一个通过对该函数的引用调用的函数。

示例代码将是:

void cbfunc()

{
        printf("called");
 }

int main ()

{

    void (*callback)(void);

    callback=(void *)cbfunc;

   callback();

   return 0;

}

现在我不明白的是,从DLL到客户端的通知是如何完全使用的。

假设我想在我的DLL方法2()上接收数据时执行/执行一些method1()。

与.NET中的事件进行任何比较都会有很好的帮助。

4 个答案:

答案 0 :(得分:1)

您将指针传递给第三方例程(不一定是DLL),并且在需要通知时被克隆指针“回调”。

它类似于.net事件,因为后者也是一种回调。

BTW,在C ++中,DLL比在.NET中更不受欢迎。这是因为不可能共享静态变量(因此也就是单例),这个问题也称为缺乏动态链接(在UNIX系统中,这是通过共享对象解决的,这是动态加载库的完全不同的概念)。静态库提供了更好的代码重用策略。

答案 1 :(得分:1)

回调函数与C#中的代理类似。

例如,Win32 API提供了一个定时服务,可以通过调用SetTimer来访问。 SetTimer由系统DLL导出,但其机制与在用户dll中使用的机制完全相同。在您的代码中,您可以通过执行以下操作来访问计时器:

void 
CALLBACK
MyTimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
// do something
}

...

TIMERPROC fn = &MyTimerCallback;
int delay = 500;  
SetTimer(NULL,0,delay,fn); 

调用SetTimer并传入回调函数,允许操作系统在每次计时器滴答时回调函数。当然,这里没有多播功能,特别是在SetTimer的情况下,回调函数必须是C函数或静态类方法。没有与该函数关联的类或对象实例。

类似的模式可以在.NET中完成 - 我确信.NET有自己的Timer范例,但是暂时我们可以假装它实现了一个带有TimerDelegate的SetTimer函数。

在用户代码中,在对象中,您可以将MyTimerProc定义为具有与委托匹配的签名的函数。并像这样调用它

TimerDelegate d = new TimerDelegate(myObject.MyTimerProc);
SetTimer(0,0,delay,d);

或者,如果“timers”是与TimerDelegate匹配的事件,那么等效的C#代码将类似于:

timers += new TimerDelegate(myObject.MyTimerProc);

注意:我的C#非常生疏,所以不要将这些代码示例作为最佳实践的任何示例,甚至是工作代码:P


在定义自己的回调函数时,最好定义回调函数以获取void *“context”参数,因为这样可以让C ++程序员存储它们的“this”指针并检索它。

// the first parameter to the callback fn is a void* user supplied context parameter
typedef void (CALLBACK* MyCallbackFn)(void* context, int etc);

// and, the dll export function always takes a function pointer, context parameter pair.
DLL_EXPORT void SomeExportedFn(MyCallbackFn, void* context);

答案 2 :(得分:1)

回调和接口类是管理代码边界的好方法。它们有助于在代码中创建正式的边界和/或层,而不是将所有内容混为一谈。在处理大型软件解决方案时,这是必要的。

下面是如何使用回调和接口类的示例。在库/ DLL代码中,唯一应该暴露给主可执行文件的是myddl_interface类和函数getMyDllInterface()。使用这样的接口类完全隐藏了主可执行文件的实现细节。接口类还允许主可执行文件向其注册一个稍后将执行的函数(即回调)。

// Begin library/dll Public Interface used by an executable
class mydll_interface {  
public:  
    typedef void (*callback_func_t)();  
public:  
    virtual void do_something() = 0;  
    virtual void registerFunction( callback_func_t ) = 0;  
};  

static mydll_interface* getMyDllInterface();

// End library/dll Public Interface used by an executable

// Begin library/dll Private implementation
class mydll_implementation : public mydll_interface {  
public:  
    void do_something() {  
        printf("Hello World\n");  
        _callback_func();  
    }  
    void registerFunction( callback_func_t c) {  
        _callback_func = c;  
    }  
private:  
    callback_func_t _callback_func;  
};  

static mydll_interface* getMyDllInterface() {  
    return new mydll_implementation();  
};  
// End library/dll Private implementation

// Begin main executable code
void myMainAppFunc() {  
    printf("hello World Again\n");  
}  

int main() {    
    mydll_interface* iface = getMyDllInterface();  
    iface->registerFunction(&myMainAppFunc);  
    iface->do_something();  
};  
// End main executable code

答案 3 :(得分:-1)

一个值得注意的例子是qSort(),它需要一个回调函数来比较2个项目。 回调函数可以在运行时决定某些行为。

如果某些行为应该由客户端在运行时动态决定,那么我将要求客户端提供回调函数。