Reputation: 3791
I want to create a TService
descendant class that I use as the basis for my Windows service implementations. In my base class I am introducing a published ServiceDescription
property, and I am using the AfterInstall
event handler to write this description to the appropriate location in the Windows registry.
Note that since the TServer
class (declared in Vcl.SvcMgr
) is a TDataModule
descendant, in order to permit the ServiceDescription
property to be visible in the Object Inspector it is necessary to declare this base class in a designtime package, and register it with Delphi using a call to RegisterCustomModule
. In addition, a descendant of this base class must be generated by an OTA (open tools api) wizard or some sort of code generator (both .pas and .dfm files). No problem, I've got that one sorted, and if you're interested you can read more about it from Marco Cantu's book (http://www.marcocantu.com/ddh/ddh15/ddh15e.htm).
Where I’m stuck is that I want to use the AfterInstall
event handler in my base class to write to the Registry, and the AfterUninstall
to remove it, but I want to ensure that my descendant classes will also support AfterInstall
and AfterUninstall
events.
I previously learned from Ray Konopka that if you want to reintroduce a property that you must use accessor methods in the descendant class. As a result, here is a code segment that represents my attempt to do this with respect to the AfterInstall
event:
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;
My overridden constructor assigns a method to the inherited AfterInstall
property:
constructor TTPMBaseService.Create(AOwner: TComponent);
begin
inherited;
// Hook-up the AfterInstall event handlers
Self.AfterInstall := CallAfterInstall;
…
end;
In my implementation of CallAfterInstall
, after I run my code to write to the Windows Registry, I test to see if a method pointer has been assigned to my local method pointer field, and if so, I call it. It looks something like this:
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;
I think that this all makes a lot of sense, and I think it should work. However, I’m stuck on the accessor methods. The Get accessor method compiles just fine, and here it is:
function TTPMBaseService.GetAfterInstall: TServiceEvent;
begin
Result := FFAfterInstall;
end;
But my SetAfterInstall
method raises a compile-time exception, reporting that there are not enough parameters:
procedure TTPMBaseService.SetAfterInstall( value: TServiceEvent );
begin
if value <> FFAfterInstall then
FFAfterInstall := value;
end;
I’m not sure what to do here. I made the following change, and it compiles, but it does not appear to do the job:
procedure TTPMBaseService.SetAfterInstall( value: TServiceEvent);
begin
if @value <> @FFAfterInstall then
@FFAfterInstall := @value;
end;
I have two questions. The first is, am I taking the correct approach to reintroducing an event handler, all the while ensuring that both my base class, as well as its descendants, support this event? If my logic is correct, what am I doing wrong with the Setter accessor method?
Upvotes: 4
Views: 375
Reputation: 3830
Am I taking the correct approach to reintroducing an event handler?
Probably yes. Thanks to clumsy design of TService
class you're not able to override a method that raises the event.
What am I doing wrong with the Setter accessor method?
The problem is in fact in your constructor:
constructor TTPMBaseService.Create(AOwner: TComponent);
begin
inherited;
// Hook-up the AfterInstall event handlers
Self.AfterInstall := CallAfterInstall;
…
end;
The comment in it indicates that you're setting inherited event handler, but that's not what the code below the comment does. Despite of assigning to Self.AfterInstall
you're setting the value of reintroduced property. This is how you set inherited property:
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.
You're getting syntax error on if
statement in the setter method to be precise. It's simply because that's not how you compare method references in Delphi. See How to check if two events are pointing to the same procedure in Delphi. Why do you even need to perform such comparison? You can safely omit it.
Upvotes: 6