Orhan Cinar
Orhan Cinar

Reputation: 8410

Delphi how to determine which procedure called the another procedure?

How can i determine who called the Showme Procedure?

procedure Showme(str:string);
begin
  ShowMessage(str);
end;

procedure btnclick(sender:TObject);
begin
  Showme("test");
end;

procedure btn2click(sender:TObject);
begin
  Showme("test");
end;

Edit : The Confusion

Showme(654, '654'); // procedure name, string
Showme(654, '564');

Upvotes: 3

Views: 4932

Answers (5)

Alex
Alex

Reputation: 5668

Well, if you are on modern Delphi:

procedure Showme(str:string);
var
  Caller: Pointer;
begin
  // This will save address of the NEXT CPU opcode AFTER the call into your function
  Caller := ReturnAddress;  
  // Shift the address, so it will be in the middle of the call into your function
  // It is kinda a hack: the proper code would move address back to the size of the call opcode itself - which is not a trivial thing to do
  // It is optional: you can skip it, if you are OK with the address of next opcode after the call
  Caller := PAnsiChar(Caller) - 1;

  ShowMessage(str);
end;

Once you have obtained the pointer to the call instruction to your function, you need to convert that address to a human-readable string. It is not possible by default, since the compiled executable does not contain function names, only RAW addresses (there are exceptions, like RTTI, but it is not a general case).

However, if you attach debug information to your executable, then you will be able to read it to convert address to name. For example, if you have EurekaLog:

uses
  EDebugInfo;

procedure Showme(str:string);
var
  Caller: Pointer;
  FunctionName: String;
begin
  Caller := ReturnAddress;  
  Caller := PAnsiChar(Caller) - 1;
  FunctionName := GetLocationInfoStr(Caller);
  ShowMessage(FunctionName + ': ' + str);
end;

However:

  1. It will only work if you are compiling with some debug information (and the corresponding debug info provider is enabled):
  • EurekaLog has its own debug info format, which can be optionally compressed (which is actually not recomended, as you will spend more memory and CPU).
  • It also supports JCL/JEDI, Synopse/MAB, as well as .map, .tds/TD32, .dbg, .pdb.
  1. It is NOT a constant. The name will be looked up dynamically, so it have some run-time costs.

P.S. Please note, that you should only do that only for logging/debugging purposes. Your app logic should not be implemented like this. You should use classes, virtual functions, RTTI, dispatch tables, or scripting engine - depending of what you are trying to do.

Upvotes: 0

mjn
mjn

Reputation: 36664

TVirtualMethodInterceptor allows to execute code before and after the actual method, and there is a Method parameter which contain the method name:

Example code:

type
  TFoo = class
  public
    procedure Bar; virtual;
  end;

procedure InterceptorBefore(Instance: TObject; Method: TRttiMethod;
  const Args: TArray<TValue>; out DoInvoke: Boolean; out Result: TValue);
begin
  ShowMessage('Before: ' + Instance.ClassName + '.' + Method.Name);
end;

procedure InterceptorAfter(Instance: TObject; Method: TRttiMethod;
  const Args: TArray<TValue>; var Result: TValue);
begin
  ShowMessage('After: ' + Instance.ClassName + '.' + Method.Name);
end;

{ TFoo }

procedure TFoo.Bar;
begin
  ShowMessage('TFoo.Bar');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  interceptor: TVirtualMethodInterceptor;
  foo: TFoo;
begin
  foo := TFoo.Create;

  interceptor := TVirtualMethodInterceptor.Create(TFoo);
  interceptor.Proxify(foo);
  interceptor.OnBefore := InterceptorBefore;
  interceptor.OnAfter := InterceptorAfter;

  foo.Bar;

end; 

Note this is new in Delphi XE so I can not test and verify it on my system.

Upvotes: 2

jachguate
jachguate

Reputation: 17203

I will assume you're running the application in the debugger, so you can put a breakpoint inside the Showme procedure and when the program stops, activate the stack view (view/debug windows/Call stack).

It will show you who called it and actually if you double click on any stack entry the IDE will walk you to the exact line of code that makes the call (in case showme is called more than once per calling routine).

BTW: I think you have to make a better effort to express your question.

Upvotes: 2

Mason Wheeler
Mason Wheeler

Reputation: 84610

There's no built-in way for one procedure to know which procedure called it. You can use stack tracing if you really need to know, but that sort of data is really only needed for debugging. For actual execution, what matters is the data that's passed to the routines, not where it came from. That's one of the basic principles of structured programming. If two different routines call the same procedure with the same data, it should treat them both the same.

What exactly are you doing where you need to be able to tell the difference? There might be a simpler way to accomplish it.

Upvotes: 9

Eduardo Mauro
Eduardo Mauro

Reputation: 1515

Why don't you pass a different constant or even the name of the procedure calling it as a parameter of Showme? It is possible to analyze the stack in order to determine the name of the calling function but it is much more complicated and you need to add information about linking (map file). Jedi's JCL has some functions that can help you but I would use an additional parameter.

Upvotes: 8

Related Questions