I always considered the implementation of abstract methods in Delphi to be a hack, mostly because there is no way (or is there?) to treat this: “[DCC Warning] W1020 Constructing instance of a class containing abstract method.” warning as a compile error. We all know that a slot is reserved in the abstract class for each virtual (and abstract) method it holds. Delphi by default initializes those slots with addresses to _AbstractError routine in System unit.
This is how it’s defined in Delphi 2009:
procedure _AbstractError; begin if Assigned(AbstractErrorProc) then AbstractErrorProc; _RunError(210); // loses return address end;
AbstractErrorProc is being set up by SysUtils (in initialization section) if you include it in your application to be handled like this:
procedure AbstractErrorHandler; begin raise EAbstractError.CreateRes(@SAbstractError); end;
Unfortunately we cannot plug-in our custom handler and hope to just bypass all abstract errors because a _RunError(210); call will be performed in any case by _AbstractError function — which would kill the application anyway.
But if you still want to hack your way through this default behavior, there are 2 ways to do it. Note that those will only work for abstract functions with no parameters — otherwise the stack will become corrupted.
Direct memory manipulation method:
type TAbstractClass = class procedure AbstractMethod; virtual; abstract; end; var PtrToAbErr : ^Pointer; C3 : ^Byte; begin { Get the pointer to the function in slot 0 of VPTR } PtrToAbErr := Pointer(TAbstractClass); { Point to the first byte of _AbstractError routine } C3 := PtrToAbErr^; { Overwrite the first byte with a RET instruction} C3^ := $C3; WriteLn('We''ve got here! Hooray!'); ReadLn; end.
This method will not work if the memory in which _AbstractError resides is write-protected. You would have to manipulate the permissions first.
The second method will work but depends on the RTL’s implementation of _AbstractError:
type TAbstractClass = class procedure AbstractMethod; virtual; abstract; end; procedure MyHndlr(); asm ADD ESP, 4 RET end; var O : TAbstractClass; begin AbstractErrorProc := MyHndlr; O := TAbstractClass.Create(); O.AbstractMethod; WriteLn('We''ve got here! Hooray!'); ReadLn; end.
This method will plug in our own AbstractErrorProc handler which will “remove” the return address from the stack (in our case the address back to _AbstractError) and then simply returns to the code that originated the call to the abstract method.
I know it’s not very useful … but it’s fun 🙂