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

Posted

技术标签:

【中文标题】回调函数在构建和 DLL 时如何有用【英文标题】:How Callback functions are useful while building and DLL 【发布时间】:2010-09-01 07:53:05 【问题描述】:

回调函数是否等同于 C#(.NET) 中的事件。

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

示例代码为:

void cbfunc()


        printf("called");
 

int main ()



    void (*callback)(void);

    callback=(void *)cbfunc;

   callback();

   return 0;

现在我不明白的是,关于从 DLL 通知客户端,这种用法如何充分。

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

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

【问题讨论】:

【参考方案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

【讨论】:

【参考方案2】:

您将指针传递给第 3 方例程(不必是 DLL),并在需要通知时通过克隆指针“回调”。

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

顺便说一句,DLL 在 C++ 中不如在 .NET 中流行。这是由于无法共享静态变量(因此,单例),这个问题也称为缺乏动态链接(在 UNIX 系统中,这是通过共享对象解决的,这是一个完全不同的动态加载库的概念)。静态库提供了更好的代码重用策略。

【讨论】:

【参考方案3】:

回调函数的目的与 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);

【讨论】:

【参考方案4】:

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

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

【讨论】:

以上是关于回调函数在构建和 DLL 时如何有用的主要内容,如果未能解决你的问题,请参考以下文章

C#调用C++ dll 回调

DELPHI调用DLL时的回调函数问题

异步回调函数

C#中调用C的DLL中的回调函数,想实现消息响应机制

如何从 C# 调用具有 void* 回调和对象参数的 C++ Dll 中的函数

回调函数和钩子函数的说明