Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Как узнать имя вызвавшей процедуры.


Автор: LamerTM 16.5.2009, 15:58
Есть процедура (допустим называется WL), которую вызывают другие процедуры. Хочется из процедуры WL узнавать имя процедуры ее вызвавшей (без передачи ей каких-то дополнительных параметров). Это возможно ?

Автор: CodeMonkey 16.5.2009, 16:04
http://www.delphikingdom.com/asp/answer.asp?IDAnswer=57424?

Автор: LamerTM 16.5.2009, 16:30
Цитата(CodeMonkey @ 16.5.2009,  16:04)
http://www.delphikingdom.com/asp/answer.asp?IDAnswer=57424?

Нет. У меня другой случай.


У меня так:


Код

type 

TGen = сlass
 procedure wl(s: string);
end;

TSec1 = сlass(TGen)
procedure Proc1;
end;

TSec2 = сlass(TGen)
procedure Procedure2;
end;

implementation

procedure TSec1.Proc1;
begin
wl('text1');
end;

procedure TSec2.Procedure2;
begin
wl('text2');
end;

procedure TGen.wl(s: string);
begin
 // вот в этом месте хотелось бы получить название процедуры, которая вызвала wl, чтобы преобразовать строку s таким образом, например: "Procedure2: text2"
end;


Для чего мне это вообще понадобилось. Я хочу сделать чтобы моя программа писала лог. Разные процедуры передают сообщения в процедуру wl в виде строк (wl пишет эти сообщения в окно либо в log-файл). Хотелось бы чтобы wl сама вычисляла имя процедуры, которая эту сроку передала, чтобы знать какая именно процедура это сообщение генерировала. Имя класса у меня уже вычисляется (по ClassName). А имя процедуры нет. Можно передавать имя процедуры прямо в строке. Но мне хочется чтобы само вычислялось.

Автор: CodeMonkey 16.5.2009, 16:50
По-умолчанию, в общем случае, этого сделать нельзя. Вы можете узнать только адрес вызывающего. Его имя же нигде в программе не хранится. Исключение составляет RTTI классов, но это решение очень узко-специализированное.

Решение в общем виде выглядит так: вы подключаете в программу отладочную информацию и извлекаете имя из неё. Один из вариантов - используя http://sourceforge.net/projects/jcl и JclDebug.
Вот ссылка для чтения:

http://forum.vingrad.ru/index.php?showtopic=235599&view=findpost&p=1696161

В JclDebug вас будут интересовать функции Caller и GetLocationInfo(Str)

Если вы в своей программе используете какой-либо трейсер исключений (EurekaLog, madExcept или аналоги) - то, конечно, лучше бы воспользоваться его функциями. Например, в EurekaLog есть GetSourceInfoByAddr.

Автор: LamerTM 16.5.2009, 17:17
Цитата(CodeMonkey @ 16.5.2009,  16:50)
По-умолчанию, в общем случае, этого сделать нельзя. Вы можете узнать только адрес вызывающего. Его имя же нигде в программе не хранится. Исключение составляет RTTI классов, но это решение очень узко-специализированное.

Решение в общем виде выглядит так: вы подключаете в программу отладочную информацию и извлекаете имя из неё. Один из вариантов - используя http://sourceforge.net/projects/jcl и JclDebug.
Вот ссылка для чтения:

http://forum.vingrad.ru/index.php?showtopic=235599&view=findpost&p=1696161

В JclDebug вас будут интересовать функции Caller и GetLocationInfo(Str)

Если вы в своей программе используете какой-либо трейсер исключений (EurekaLog, madExcept или аналоги) - то, конечно, лучше бы воспользоваться его функциями. Например, в EurekaLog есть GetSourceInfoByAddr.

ОК, там много чего читать, поэтому я пока еще один вопрос задам.

Имея адрес процедуры можно узнать ее имя функцией MethodName. В данном случае мне этого достаточно, т.к. у меня везде классы. Я так когда-то делал обработчик исключительных ситуаций. Но он получал информацию из объекта Exception, который содержал адрес исключения ExceptionAddress. По этому адресу определялась процедура в которой произошло исключение.

А сейчас адреса нет. Но по идее адрес вызывающей процедуры должен оставаться где-то в стеке (вернее адрес места вызова процедуры wl). Как-нибудь можно из стека этот адрес получить? Если получится, то имя процедуры вычислить будет несложно. Это будет самое лучшее решение для меня, т.к. самое простое.

Автор: CodeMonkey 16.5.2009, 18:32
Вот один из возможных вариантов:

Код
type
  PStackFrame = ^TStackFrame;
  TStackFrame = record
    CallersEBP: DWORD;
    CallerAdr: DWORD;
  end;

{$STACKFRAMES OFF}

function GetEBP: Pointer;
asm
        MOV     EAX, EBP
end;

function GetStackTop: DWORD;
asm
        MOV     EAX, FS:[4]
end;

{$STACKFRAMES ON}

function Caller(Level: Integer): Pointer;
var
  TopOfStack: Cardinal;
  BaseOfStack: Cardinal;
  StackFrame: PStackFrame;
begin
  Result := nil;
  try
    StackFrame := GetEBP;
    BaseOfStack := Cardinal(StackFrame) - 1;
    TopOfStack := GetStackTop;
    while (BaseOfStack < Cardinal(StackFrame)) and (Cardinal(StackFrame) < TopOfStack) do
    begin
      if Level = 0 then
      begin
        Result := Pointer(StackFrame^.CallerAdr - 1);
        Break;
      end;
      StackFrame := PStackFrame(StackFrame^.CallersEBP);
      Dec(Level);
    end;
  except
    Result := nil;
  end;
end;

Автор: LamerTM 16.5.2009, 20:54
О! Спасибо огромное. Как раз то что нужно.
Правда оказалось что теперь везде надо понаставить published, ну да ладно. Все равно я в силу своего ламерства не знаю какие это имеет издержки  smile и имеет ли.
Спасибо еще раз.

Автор: CodeMonkey 16.5.2009, 22:06
Как я уже говорил - обычно это решается подключением отладочной информации к программе, а не использованием RTTI. Но тут уж ваш выбор.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)