Search code examples
delphidelphi-7delphi-5

Patching a public non-virtual method


I need to patch a public method for the entire application, replace it with my own but still be able to call the original method from my patched method.

I know how to replace the method with my own. How to change the implementation (detour) of an externally declared function

And here another example: Make Disabled Menu and Toolbar Images look better?

But what I don't know is how to call the original first. e.g.

// Store the original address of the method to patch
var OriginalMethodBackup: TXRedirCode;     

// this is the implementation of the new method
procedure MyNew_Method(Self: TObject; Index: Integer);
begin
  try
    // some code here
    call ORIGINAL public method here
  finally
    // some code here
  end;
end;

EDIT: I have tried Delphi Detours library, but it wont compile under D5 and D7. there are numerous issues, such as pointer arithmetic, unknown types, class vars, unknown compiler directives, unknown asm instructions, etc... code should be ported to support both D5 and D7 (strangely the author states it supports D7). I have done most of the porting by now, but still stuck on some issues. In any case I'm not sure it will even work properly after I'm done. so alternative might be needed.


Solution

  • If you want to change a non-virtual method you'll need to patch the method.

    You can patch the routine using the Delphi Detours library:

    type
      TMyMethod = procedure(Self: TObject; Index: Integer);
    
    var
      OldMethod: TMyMethod;  //make sure to initialize to nil.
    
    procedure MyNewMethod(Self: TObject; Index: Integer);
    begin
      Assert(Assigned(OldMethod),'Error: MyMethod is not hooked');
      //If you need to call the OldMethod you can use the variable. 
      OldMethod(Self, Index);
      //Do additional stuff here
      ShowMessage('New stuff');
    end;
    
    procedure DoHook;
    begin
      @OldMethod:= InterceptCreate(@MyOriginalMethod, @MyNewMethod);
    end;
    
    procedure RemoveHook;
    begin
      if Assigned(OldMethod) then
      begin
        InterceptRemove(@OldMethod);
        OldMethod := nil;
      end;
    end;
    

    A link to the wiki for DDetours is here.

    Whilst the method is patched you can call the OldMethod to access the original method.