在使用package编译的dll和没有它们编译的dll之间处理回调方法是安全的(Delphi)?

时间:2012-04-23 06:25:37

标签: delphi dll callback

我正在使用Delphi 2007,我有这个案例:

{ CommonUnit.pas }
type
  // there is a callback which I want to process
  TFooBar = procedure(Sender: IInterface) of object; stdcall;

  // there is an interface which is used by all modules
  IFoo = interface
  ['{0FAA4B2B-E82A-4A2A-B55F-C75EC53A1318}']
    procedure Bar(Callback: TFooBar); stdcall;
  end;

{ UnitInModuleCompiledWithoutPackages.pas }
type
  // there is a class which implements IFoo
  // and it's defined in Module One compiled without packages
  TFoo = class(TInterfacedObject, IFoo)
  public
    // implementation is ommited
    procedure Bar(Callback: TFooBar); stdcall;
  end;

{ UnitInModuleCompiledWithPackages.pas }
// there is a code in Module Two compiled with packages
type
  TSomeClass = class
  public
    // implementation is ommited
    procedure SomeMethod(Sender: IInterface); stdcall;
  end;

var
  SomeObject: TSomeClass; // assigned by somehow
  Foo: IFoo; // assigned by somehow

begin
  // ...
  Foo.Bar(SomeObject.SomeMethod); // so it is safe?
  // ...
end;

我知道如果我在Foo.Bar中尝试传递对象引用,如果它被声明为这样,那么在我的情况下会出现内存损坏:

type
  IFoo = interface
  ['{0FAA4B2B-E82A-4A2A-B55F-C75EC53A1318}']
    // TSomeClass now declared in CommonUnit.pas
    procedure Bar(CallbackObject: TSomeClass); stdcall;
  end;

这是因为模块一中的TSomeClass的实现与模块二(不同的内存管理器等)的实现不同。
但是方法参考呢? 我没有在Embarcadero的文档中找到任何可能用来清理这些内容的东西。

2 个答案:

答案 0 :(得分:5)

你的代码很好。当你传递方法指针TFooBar时,你传递两个指针,一个函数指针和一个实例指针。当您调用该方法时,所有版本的Delphi都会执行完全相同的操作来调用该方法,因为调用约定强制执行精确的二进制接口。并且所有版本的Delphi都以相同的方式表示方法指针。

您关心的问题是:

  1. 不同的内存管理器。这里没问题,因为我们没有进行堆分配。
  2. 不同编译器上的不同对象表示。这里没问题,因为调用方法指针不依赖于对象表示。它依赖于代码指针和数据指针(通过模块之间的值传递)和调用约定(按照约定同意相同)。

答案 1 :(得分:3)

正如David Heffernan已经回答的那样,使用方法指针很好。使用任何类型的“回调对象”而不是方法指针的唯一原因是:

  • 在Bar返回后,IFoo实例可能会保留指针,在这种情况下,回调最好被描述为事件,并且您应该传入事件接收器 interface,保证适当的生命周期管理。
  • IFoo.Bar的实现可能会调用多种替代回调方法,在这种情况下,您还应该考虑传入单个接口(不是类实例,因为您自己陈述的原因)而不是多个方法指针。