2012-03-12 63 views
4

我正在开发打算跨平台的应用程序。我曾经使用Windows消息,但现在我放弃了。我用回调替换了消息,但是不管我能否使用不同的技术,我都没有意识到不使用Windows消息时的不同可能性。通知有关从dll到主应用程序的事件

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

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

所以我的问题是:

  • 有什么选择(跨平台)到窗口消息?回调可以取代消息吗?
  • 其他语言是否支持回调对象?
  • 我猜其他语言有不同的技术作为消息的替代?

回答

2

所以我的问题是: 什么是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
+0

如果DLL知道EXE要存储在句柄参数中的东西的类型,那么DLL甚至会使用*该类型的句柄参数,那么你已经错过了句柄点。提供句柄的模块应允许存储*任何*数据类型,而不用担心句柄的使用者以任何方式使用句柄,除非将句柄传回给句柄提供者。在你的例子中,EXE必须存储一个有效的TObject引用。如果没有,那么DoSomething会崩溃。也可以直接传递TObject而不是THandle charade。 – 2012-03-12 22:15:26

+0

并传递TObject参考“提供与不同语言的兼容性”和可移植性?!?我只是展示了最简单的句柄实现,仅此而已。 – g2mk 2012-03-12 23:26:14

+0

那么我的问题很简单。我不需要传递我只需要通知主应用程序一些更改的对象,因此我可以刷新GUI。我正在寻找跨平台的解决方案,可以与其他语言一起使用。我想我会用回调来完成它。我只需要一些批准和不同方面来解决我的问题。谢谢。 – Nix 2012-03-13 11:27:33

3

我肯定会使用回调。主应用程序可以给DLL回调函数以在需要时调用,然后回调函数本身可以发送窗口消息给应用程序(如果需要的话)。

4

你当然可以使用回调函数而不是消息。您不能使用回调方法,因为只有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. 当初始化它们时,传递每个插件的引用。

    ​​

    插件应该存储到收听对象的引用,并且当数据结构的变化,它可以调用Call方法来通知应用程序。

什么我这里描述的是什么通常被称为事件接收。你的程序中可以有多个。如果有多个事件需要处理,您可以为每种事件分别设置一个接口,或者将它们全部组合到一个接口中,并针对每个事件采用不同的方法。每个插件可以有不同的接收器对象,也可以为每个插件分配一个对同一个接收器对象的引用,然后传递一个插件ID参数。

3

我同意雷米(!)。一个简单的回调允许处理程序实现它选择的任何进一步的通信 - 它可能会发布消息,它可能会将参数推送到队列中,无论它想要什么。如果你想要跨平台,你将不得不求助于传入和传出简单的类型。当设置回调时,通常会传入“用户上下文”指针。回调将该指针传递给处理程序。这允许调用者作为指针/ int传入上下文对象,并在处理程序中恢复它(通过将指针/ int转换回对象)。然后处理程序可以在上下文中调用方法,不管它是Delphi还是C++等。

相关问题