Delphi 通知从dll到主应用程序的事件

Delphi 通知从dll到主应用程序的事件,delphi,dll,callback,messages,Delphi,Dll,Callback,Messages,我正在开发打算跨平台的应用程序。我以前使用Windows消息,但现在我放弃了它。我用回调替换了消息,但不管我可以使用不同的技术,我不知道不使用windows消息时会有不同的可能性 我有主要的exe应用程序和一些dll插件。我在dll中有一些对象和线程,我想通知主应用程序dll对数据结构所做的一些更改 正如我所说,我目前正在处理一些回调。为了提供与不同语言(C++、VB、C#)的兼容性,我使用了非对象类型的回调。我不确定其他语言是否支持对象的回调 因此,我的问题是: windows消息的替代方案

我正在开发打算跨平台的应用程序。我以前使用Windows消息,但现在我放弃了它。我用回调替换了消息,但不管我可以使用不同的技术,我不知道不使用windows消息时会有不同的可能性

我有主要的exe应用程序和一些dll插件。我在dll中有一些对象和线程,我想通知主应用程序dll对数据结构所做的一些更改

正如我所说,我目前正在处理一些回调。为了提供与不同语言(C++、VB、C#)的兼容性,我使用了非对象类型的回调。我不确定其他语言是否支持对象的回调

因此,我的问题是:

  • windows消息的替代方案(跨平台)是什么?回调可以代替消息吗
  • 其他语言是否支持对象的回调
  • 我猜其他语言有不同的技术来替代消息

    • 我肯定会使用回调。主应用程序可以向DLL提供回调函数,以便在需要时调用,然后回调函数本身可以在需要时向应用程序发送窗口消息

      因此,我的问题是: windows消息的替代方案(跨平台)是什么?回调可以代替消息吗

      是的,您可以用回调替换消息

      其他语言是否支持对象的回调

      您不应该将对象方法用作回调。可移植代码中的常见做法是使用句柄(通知调用约定):

      DLL源:

      type
        THandle = LongWord;
        {$IF SizeOf(THandle) < SizeOf(Pointer))}
        {$MESSAGE Error 'Invallid handle type'}
        {$ENDIF}
      
        TCallback = procedure(const aHandle: THandle); cdecl;
      
        var
            gCallback: record
              Routine: TCallback;
              Obj: TObject;
              Info: string
        end;
      
        function Object2Handle(const aObj: TObject): THandle;
        begin
          Result:= THandle(Pointer(aObj))
        end;
      
        function Handle2Object(const aHandle: THandle; out aObj: TObject): Boolean;
        begin
          if gCallback.Obj <> nil then
            if aHandle = Object2Handle(gCallback.Obj) then
            begin
              aObj:= gCallback.Obj;
              Result:= true;
              Exit // WARRNING: program flow disorder
            end;
      
          aObj:= nil;
          Result:= false
        end;
      
      procedure DoCallback();
      begin
        if Assigned(gCallback.Routine) then
          gCallback.Routine(Object2Handle(gCallback.Obj))
      end;
      
      procedure SetupCallback(const aCallback: TCallback); cdecl;
      begin
        gCallback.Routine:= aCallback;
      end;
      
      procedure DoSomething(const aHandle: THandle; out aInfo: string); cdecl;
      var
        O: TObject;
      begin
        if Handle2Object(aHandle, O) then
          aInfo:= Format('%s class object %s', [O.ClassName(), gCallback.Info])
      end;
      
      procedure Test();
      begin
        gCallback.Obj:= TStream.Create();
        try
          gCallback.Info:= 'created';
          DoCallback();
        finally
          FreeAndNil(gCallback.Obj)
        end;
        gCallback.Obj:= TMemoryStream.Create();
        try
          gCallback.Info:= 'will be freed';
          DoCallback();
        finally
          FreeAndNil(gCallback.Obj)
        end
      end;
      
      exports
        SetupCallback,
        DoSomething,
        Test;
      
      编辑:你现在不能在腿上开枪

      我猜其他语言有不同的技术来替代消息

      操作系统有一些消息选择。然而,真正便携式的并不多

      您还可以使用:

      • 插座
      • (在这种情况下,IMO太大了?)就绪消息传递系统(我最喜欢的)

        • 您当然可以使用回调函数而不是消息。不能使用回调方法,因为只有Delphi和C++ Builder了解如何调用Delphi方法指针。但是,您可以将回调对象与支持COM的任何语言一起使用。下面是一个插件通知应用程序数据结构已更改的示例:

        • 定义一个接口

          type
            IDataStructureChanged = interface
              ['{GUID}']
              procedure Call; stdcall;
            end;
          
          您可以向方法中添加一些参数,以便插件可以告诉数据结构是如何更改的,或者传递一些值,指示哪个插件正在发出通知

        • 在应用程序中实现它

          type
            TDataStructureChangedListener = class(TInterfacedObject, IDataStructureChanged)
            private
              FForm: TForm;
              procedure Call; stdcall;
            public
              constructor Create(Form: TForm);
            end;
          
          当实例化该类时,可以向其传递对程序主窗体的引用,或者当插件最终调用
          Call
          方法时,程序能够执行操作所需的任何其他信息。实现
          Call
          ,使应用程序在数据结构发生更改时执行任何需要执行的操作

        • 初始化每个插件时,传递对它们的引用

          ChangeListener := TDataStructureChangedListener.Create(Self);
          for i := 0 to Pred(PlugIns.Count) do
            PlugIns[i].Init(ChangeListener);
          
          插件应该存储对侦听器对象的引用,当数据结构发生更改时,它可以调用
          call
          方法来通知应用程序

        • type
            TDataStructureChangedListener = class(TInterfacedObject, IDataStructureChanged)
            private
              FForm: TForm;
              procedure Call; stdcall;
            public
              constructor Create(Form: TForm);
            end;
          

          我在这里描述的是通常所说的事件接收器。您的程序中可以有多个。如果要处理多个事件,则可以为每种类型的事件使用单独的接口,也可以将它们全部分组到单个接口中,并为每个事件使用不同的方法。您可以为每个插件使用不同的接收器对象,也可以为每个插件提供对同一接收器对象的引用,然后传递一个插件ID参数。

          我同意Remy,(!)。一个简单的回调允许处理程序实现它选择的任何类型的进一步通信——它可以发布消息,可以将参数推送到队列上,不管它想要什么。如果您想成为跨平台的,您将不得不求助于传入和传出简单类型。设置回调时,通常会传入“用户上下文”指针。回调函数将此指针传递给处理程序。这允许调用者将上下文对象作为指针/int传递,并在处理程序中恢复它(通过将指针/int强制转换回对象)。然后,处理程序可以在上下文中调用方法,不管它是Delphi、C++等等。如果DLL知道EXE将存储在句柄参数中的类型,那么DLL甚至使用句柄参数作为该类型,那么您就错过了句柄点。应该允许提供句柄的模块在其中存储任何数据类型,而不必担心句柄的使用者以任何方式使用它,除非将其传递回句柄提供程序。在您的示例中,EXE必须存储有效的ToObject引用。如果没有,那么DoSomething将崩溃。也可以直接传递TObject而不是这个THandle字谜。传递TObject引用“提供与不同语言的兼容性”和可移植性?!?我只是展示了可能的最简单的句柄实现,仅此而已。我的问题非常简单。我不需要传递对象,我只需要将一些更改通知主应用程序,以便刷新GUI。我正在寻找解决方案,是跨平台的,可以与其他语言使用。我想我会通过回调来实现。我只是需要一些认可和我的问题的不同方面。谢谢