在 Delphi 中,如何在基础 class 及其后代 classes 中启用方法指针 属性

In Delphi, how do I enable a method pointer property in both a base class and its descendant classes

我想创建一个 TService 后代 class 作为我的 Windows 服务实现的基础。在我的基础 class 中,我正在引入一个已发布的 ServiceDescription 属性,并且我正在使用 AfterInstall 事件处理程序将此描述写入 [=56= 中的适当位置] 注册表。

请注意,由于 TServer class(在 Vcl.SvcMgr 中声明)是 TDataModule 后代,为了允许 ServiceDescription 属性 要在对象检查器中可见,必须在设计时包中声明此基 class,并使用对 RegisterCustomModule 的调用将其注册到 Delphi。此外,此基础 class 的后代必须由 OTA(打开工具 api)向导或某种代码生成器(.pas 和 .dfm 文件)生成。没问题,我已经整理好了,如果您有兴趣,可以从 Marco Cantu 的书中阅读更多相关信息 (http://www.marcocantu.com/ddh/ddh15/ddh15e.htm)。

我被卡住的地方是我想使用我的基础 class 中的 AfterInstall 事件处理程序写入注册表,并使用 AfterUninstall 删除它,但是我想确保我的后代 classes 也将支持 AfterInstallAfterUninstall 事件。

我之前从 Ray Konopka 那里了解到,如果你想重新引入一个 属性,你必须在后代 class 中使用访问器方法。因此,这是一个代码段,代表我对 AfterInstall 事件所做的尝试:

  private
    // field to store method pointer for the descendant AfterInstall event handler
    FFAfterInstall: TServiceEvent;
    …
  protected
    function GetAfterInstall: TServiceEvent;
    procedure SetAfterInstall( value: TServiceEvent );
    …
  published
    property AfterInstall: TServiceEvent read GetAfterInstall write SetAfterInstall;

我重写的构造函数为继承的 AfterInstall 属性:

分配了一个方法
constructor TTPMBaseService.Create(AOwner: TComponent);
begin
  inherited;
  // Hook-up the AfterInstall event handlers
  Self.AfterInstall := CallAfterInstall;
  …
end;

在我 CallAfterInstall 的实现中,在我 运行 我的代码写入 Windows 注册表后,我测试是否已将方法指针分配给我的本地方法指针字段,如果是这样,我就调用它。它看起来像这样:

procedure TTPMBaseService.CallAfterInstall(Service: TService);
var
  Reg: TRegistry;
begin
  // Code here to write to the Windows Registry is omitted
  // Test if our method pointer field has been written to
  if Assigned( FFAfterInstall ) then
    FFAfterInstall( Service );  // call the method,
  …
end;

我认为这一切都很有道理,而且我认为它应该可行。但是,我坚持访问器方法。 Get 访问器方法编译得很好,这里是:

function TTPMBaseService.GetAfterInstall: TServiceEvent;
begin
  Result := FFAfterInstall;
end;

但是我的 SetAfterInstall 方法引发了编译时异常,报告没有足够的参数:

procedure TTPMBaseService.SetAfterInstall( value: TServiceEvent );
begin
  if value <> FFAfterInstall then
    FFAfterInstall := value;
end;

我不知道该做什么。我做了以下更改,它编译了,但它似乎没有完成这项工作:

procedure TTPMBaseService.SetAfterInstall( value: TServiceEvent);
begin
  if @value <> @FFAfterInstall then
    @FFAfterInstall := @value;
end;

我有两个问题。首先是,我是否采用了正确的方法来重新引入事件处理程序,同时确保我的基础 class 及其后代都支持此事件?如果我的逻辑是正确的,那么 Setter 访问器方法我做错了什么?

Am I taking the correct approach to reintroducing an event handler?

可能是的。由于 TService class 的笨拙设计,您无法覆盖引发事件的方法。

What am I doing wrong with the Setter accessor method?

问题实际上出在您的构造函数中:

constructor TTPMBaseService.Create(AOwner: TComponent);
begin
  inherited;
  // Hook-up the AfterInstall event handlers
  Self.AfterInstall := CallAfterInstall;
…
end;

其中的注释表明您正在设置继承的事件处理程序,但这不是注释下面的代码所做的。尽管分配给 Self.AfterInstall,但您正在设置重新引入的值 属性。这就是您设置继承 属性:

的方式
constructor TTPMBaseService.Create(AOwner: TComponent);
begin
  inherited;
  // Hook-up the AfterInstall event handlers
  inherited AfterInstall := CallAfterInstall;
…
end;

But my SetAfterInstall method raises a compile-time exception, reporting that there are not enough parameters.

准确地说,setter 方法中的 if 语句出现语法错误。这只是因为这不是您在 Delphi 中比较方法引用的方式。参见 How to check if two events are pointing to the same procedure in Delphi。为什么你甚至需要进行这样的比较?您可以放心地省略它。