【问题标题】:The most elegant way to encapsulate WinAPI callbacks inside a class [duplicate]将 WinAPI 回调封装在类中的最优雅方法 [重复]
【发布时间】:2010-06-01 20:26:46
【问题描述】:

我正在考虑以优雅的方式将 WinAPI 回调封装在一个类中。假设我正在创建一个处理异步 I/O 的类。所有 Windows 回调都应该是 stdcall 函数,而不是类方法(例如,我需要将它们的地址传递给 ReadFileEx WinAPI 函数)。所以,我不能只将方法地址作为回调例程传递给 WinAPI 函数。

将这种类型的功能封装在类中的最优雅的方法是什么,以便该类具有事件 OnReadCompleted 和 OnWriteCompleted(我使用 Delphi 作为主要语言,但我想 C++ 中的情况必须相同,因为类方法和简单的方法不同,它的第一个隐藏参数是this链接。当然这个类不是单例的,app可以同时创建多个.

您认为实现这一点的好方法是什么?

【问题讨论】:

    标签: delphi winapi callback


    【解决方案1】:

    我怀疑这无论如何都是优雅的,但是,IMO,最简单的方法是将类的方法的地址转换为过程地址并将其传递给 winapi。当然,这是一个 hack,但 VCL 对 classes.MakeObjectInstance 的作用相同,如果只是针对特定构造的话。请参阅 this question 以获取 source 以了解这种实现以及其他一些更多的 OO 方法来处理情况。

    【讨论】:

    • 我想知道这种方法是否适用于(远?)未来的 64 位 delphi 版本(Proc 调用在 64 中非常不同)。我真的很怀疑。但现在没有办法说。
    【解决方案2】:

    您可以为此使用 static 关键字。但它仅在新的 Delphi 版本中可用。

    像这样:

    type
      TMyThread = class
      private
        // ...
    
        class function ThreadProc(Param: Pointer): DWord; stdcall; static; // <- WinAPI call back
    
        function Execute: DWord; // <- actual callback
      public
        constructor Create;
        // ...
      end;
    
    { TMyThread }
    
    constructor TMyThread.Create;
    begin
      // ...
      FHandle := CreateThread(nil, 0, @ThreadProc, Self, 0, FID);
    end;
    
    class function TMyThread.ThreadProc(Param: Pointer): DWord;
    begin
      Result := TMyThread(Param).Execute;
    end;
    
    function TMyThread.Execute: DWord;
    begin
      MessageBox(0, 'Hello from thread', 'Information', MB_OK or MB_ICONINFORMATION);
      Result := 0;
    end;
    

    这里:ThreadProc 是 WinAPI 回调例程。它需要有某种形式的自定义参数,您可以在其中传递 Self。它不能访问实例成员。这就是为什么它只是一个真正的回调(Execute)的包装器,它是类的一部分,可以访问它的字段和方法。

    【讨论】:

    • 如果您确保回调 (Execute) 将具有与需要 WinAPI 回调的完全相同的二进制签名,您也可以摆脱包装器 (ThreadProc)。在上面的示例中,您可以将“stdcall”添加到 Execute 并完全删除 ThreadProc,将 @Execute 传递给 CreateThread。但这是一条危险的道路。
    • 谢谢,但我提到了,我的类不是单例的,应用程序中可以有很多此类的实例。所以,静态方法解决方案不是很好。除非我可能会在某处存储需要根据传递给回调的内容调用方法指针的列表。
    • @FractalizeR 嗯...为什么你认为你只能创建一个实例?您可以使用任意数量的实例。请更仔细地研究我的例子。确切的实例已保存。
    • @Alexander - 并非所有 api 回调都使用“回传”参数。例如,对于 CBTProc (SetWindowsHookEx) 回调,您将如何调整这种方法?
    • @Sertac Akyuz - @FractalizeR 询问了一个关于 ReadFileEx 的具体问题 - 您可以将 Self 传递给 ReadFileEx 的回调(通过 hEvent)。我只回答了那个特定的问题。如果您问 不同 问题(例如,我如何使用没有自定义参数的回调来做到这一点) - 那么这个答案将不适用(显然)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多