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



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

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

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

所以我的问题是:

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

您当然可以使用回调函数而不是消息。您不能使用回调方法,因为只有Delphi和C++Builder了解如何调用Delphi方法指针。但是,您可以将回调对象与任何支持COM的语言一起使用

  1. 定义一个接口。

    type
      IDataStructureChanged = interface
        ['{GUID}']
        procedure Call; stdcall;
      end;
    

    您可以向该方法添加一些参数,这样插件就可以告诉数据结构是如何更改的,或者传递一些值来指示哪个插件正在发出通知。

  2. 在应用程序中实现它。

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

    当您实例化该类时,您可以向它传递对程序主窗体的引用,或者当插件最终调用Call方法时,程序需要执行的任何其他信息。实现Call,使您的应用程序在数据结构发生更改时可以执行它需要执行的任何操作。

  3. 在初始化每个插件时,将引用传递给它们。

    ChangeListener := TDataStructureChangedListener.Create(Self);
    for i := 0 to Pred(PlugIns.Count) do
      PlugIns[i].Init(ChangeListener);
    

    插件应该存储对侦听器对象的引用,当数据结构发生变化时,它可以调用Call方法来通知您的应用程序。

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

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

我同意雷米的观点,(!)。简单的回调允许处理程序实现它选择的任何类型的进一步通信——它可以发布消息,也可以将参数推送到队列中,无论它想要什么。如果你想成为跨平台的,你就必须采用传入和传出的简单类型。在设置回调时,通常会传入"用户上下文"指针。回调将此指针传递到处理程序中。这允许调用方将上下文对象作为指针/int传入,并在处理程序中恢复它(通过将指针/int强制转换回对象)。然后,无论是Delphi、C++等,处理程序都可以调用上下文上的方法。

所以我的问题是: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;

可执行文件来源:

procedure Cb(const aHandle: THandle); cdecl;
const
  STUPID: THandle = 1;
  EQUALLY_STUPID = $DEAD;
var
  S: string;
begin
  DoSomething(STUPID, S);
  DoSomething(aHandle, S);
  DoSomething(EQUALLY_STUPID, S)
end;
begin
  SetupCallback(@Cb);
  Test()
end.

伊迪丝:你现在不能开枪打自己的腿了。

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

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

您也可以使用:

  • 插座
  • (在这种情况下,IMO太大了?)准备好的消息系统(我最喜欢的0MQ)

最新更新