В Delphi как включить свойство указателя метода как в базовом классе, так и в его классах-потомках - PullRequest
4 голосов
/ 25 февраля 2020

Я хочу создать класс-потомок TService, который я использую в качестве основы для моих Windows реализаций службы. В моем базовом классе я представляю опубликованное свойство ServiceDescription и использую обработчик событий AfterInstall, чтобы записать это описание в соответствующее место в реестре Windows.

Обратите внимание, что, поскольку * Класс 1006 * (объявленный в Vcl.SvcMgr) является потомком TDataModule, для того чтобы свойство ServiceDescription было видно в Инспекторе объектов, необходимо объявить этот базовый класс в пакете designtime и зарегистрировать его с помощью Delphi используя звонок на RegisterCustomModule. Кроме того, потомок этого базового класса должен генерироваться мастером OTA (open tools api) или генератором кода (файлы .pas и .dfm). Нет проблем, у меня есть что-то отсортированное, и если вам интересно, вы можете прочитать об этом больше из книги Марко Канту (http://www.marcocantu.com/ddh/ddh15/ddh15e.htm).

Где я застрял я хочу использовать обработчик событий AfterInstall в своем базовом классе для записи в реестр и AfterUninstall для его удаления, но я хочу убедиться, что мои классы-потомки также будут поддерживать AfterInstall и AfterUninstall events.

Ранее я узнал от Рэя Конопки, что если вы хотите снова ввести свойство, вы должны использовать методы доступа в классе-потомке. В результате вот фрагмент кода, который представляет мою попытку сделать это в отношении события 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;

У меня два вопроса. Во-первых, правильно ли я отношусь к повторному представлению обработчика событий, при этом гарантируя, что и мой базовый класс, и его потомки поддерживают это событие? Если мой лог c правильный, что я делаю не так с методом доступа Setter?

1 Ответ

6 голосов
/ 25 февраля 2020

Правильный ли я подход к повторному введению обработчика событий?

Возможно, да. Благодаря неуклюжей конструкции класса TService вы не можете переопределить метод, вызывающий событие.

Что я делаю не так с методом доступа Setter?

Проблема на самом деле в вашем конструкторе:

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;

Но мой метод SetAfterInstall вызывает исключение во время компиляции, сообщая, что не хватает параметров.

You ' Получаем синтаксическую ошибку в операторе if в методе установки, если быть точным. Это просто потому, что вы не сравниваете ссылки на методы в Delphi. См. Как проверить, указывают ли два события на одну и ту же процедуру в Delphi. Зачем вам вообще нужно такое сравнение? Вы можете смело пропустить это.

...