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 🙂