3

我正在使用 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 的文档中没有找到任何可以解决这些问题的内容。

4

2 回答 2

5

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

您关心的问题是:

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

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

  • IFoo 实例可能会在 Bar 返回后保留指针,在这种情况下,最好将回调描述为event,并且您应该传入一个事件接收器接口,以保证正确的生命周期管理。
  • IFoo.Bar 的实现可能会调用多种替代回调方法,在这种情况下,您还应该考虑传入单个接口(不是类实例,因为您声明自己)而不是多个方法指针。
于 2012-04-23T07:17:40.260 回答