3

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.

Community
  • 1
  • 1
zig
  • 4,524
  • 1
  • 24
  • 68

2 Answers2

4

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.

Johan
  • 74,508
  • 24
  • 191
  • 319
  • Thanks, but what if I can't use DDetours lib? (for example in my D5 application). is there anything could be done based on the pattern I already use? – zig Jul 13 '16 at 12:16
  • You listed D7 as your version, but apart from that as far as I know DDetours works fine in D5. – Johan Jul 13 '16 at 12:20
  • It does not work in D5. I just tested it. If no other solution I'll accept your answer, nut I was really hoping for a solution based on the initial code. – zig Jul 13 '16 at 13:21
  • @zig why not put some effort in to do this yourself – David Heffernan Jul 14 '16 at 07:25
  • @DavidHeffernan, to do what? – zig Jul 14 '16 at 08:16
  • You've got the code for a library that does precisely what you need. What is stopping you from solving your problem? Or do you want us to write your code for you? – David Heffernan Jul 14 '16 at 08:17
  • @DavidHeffernan, I'm trying to port DDetours to D5, but it's not an easy task for now... In any case I'm still wondering how could a Trampoline be implemented base on the code I showed (Your code actually :) ) – zig Jul 14 '16 at 10:27
  • 1
    @Johan, just so you know, it wont even compile in D7 – zig Jul 14 '16 at 16:01
  • @Johan, not only. there are numerous issues, such as 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... – zig Jul 15 '16 at 09:48
  • @zig, post your code to a github repo and tell me the address I'll have a look at it. – Johan Jul 15 '16 at 12:20
  • I managed to port it to D5 and D7 with great effort. I will upload it as soon as I'm done testing. thanks. – zig Jul 18 '16 at 12:38
0

Seems straight forward to me:

  • revert patch
  • call method
  • apply patch again

Edit: As others pointed out, this is only a valid approach for use in a single thread.

Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
  • 3
    That actually is not a very good advice because there goes any thread safety down the drain with such approach. – Stefan Glienke Jul 13 '16 at 11:16
  • This looks like a bad idea, because it seems to me the OP wants to call the old method inside the patched method. – Johan Jul 13 '16 at 11:24
  • @Johan, I think Uwe is saying (pseudo): `RemoveHook; call method; DoHook;` **inside** the patched method. this actually works. but I'm not sure it safe. Can't we somehow call the `OriginalMethodBackup: TXRedirCode` inside the patched method without reverting/applying the patch each time? – zig Jul 13 '16 at 12:09
  • @zig, that's a silly way of doing things and kills multi-threading. – Johan Jul 13 '16 at 12:11
  • 1
    @Johan "inside patched method" actually means "inside new replacement method" - in the old procedure only first 3 bytes have to be patched into `JMP ReplacementNewMethod` and when the execution flow would be inside that new method (which would as you suggest call de-patching) it would already be far past those 3 bytes. The re-entrance/multithreading issue is real problem though. – Arioch 'The Jul 13 '16 at 13:04
  • 1
    @Arioch'The, The patching involves pausing all threads and numerous calls to WinAPI functions. This will seriously affect performance if the function is anywhere near the critical path. – Johan Jul 13 '16 at 15:01