Ну никогда не говори никогда :-). Эта функция вернет имя метода (в форме ‹ClassType›.‹MethodName›, т. е. TMainForm.FormCreate) для события, которое вы передаете в качестве параметра. К сожалению, вы не можете использовать нетипизированный параметр, чтобы разрешить передачу любого события, но должны закодировать определенную процедуру для каждой сигнатуры метода, которую вы хотите «декодировать»:
FUNCTION MethodName(Event : TValidationFunc) : STRING;
VAR
M : TMethod ABSOLUTE Event;
O : TObject;
CTX : TRttiContext;
TYP : TRttiType;
RTM : TRttiMethod;
OK : BOOLEAN;
BEGIN
O:=M.Data;
TRY
OK:=O IS TObject;
Result:=O.ClassName
EXCEPT
OK:=FALSE
END;
IF OK THEN BEGIN
CTX:=TRttiContext.Create;
TRY
TYP:=CTX.GetType(O.ClassType);
FOR RTM IN TYP.GetMethods DO
IF RTM.CodeAddress=M.Code THEN
EXIT(O.ClassName+'.'+RTM.Name)
FINALLY
CTX.Free
END
END;
Result:=IntToHex(NativeInt(M.Code),SizeOf(NativeInt)*2)
END;
Используйте это так:
For valid In Functions Doc Begin
res := -1;
Try
res := valid(MyObject);
Except
On E: Exception Do
Log('Error in function '+MethodName(valid)+' : ' + E.Message, TNiveauLog.Error, 'PHVL');
End;
Result := Result And (res = 0);
End;
Я не пробовал это с приведенным выше кодом, но пробовал с FormCreate моей MainForm.
Есть небольшое предостережение: это будет работать только для методов, которые сгенерировали RTTI, и только для Delphi 2010 и выше (где они значительно увеличили объем данных, доступных для RTTI). Поэтому, чтобы убедиться, что это работает, вы должны поместить методы, которые хотите отслеживать, в раздел PUBLISHED, так как эти методы всегда (по умолчанию) будут генерировать RTTI.
Если вы хотите, чтобы это было немного более общим, вы можете использовать эту конструкцию:
FUNCTION MethodName(CONST M : TMethod) : STRING; OVERLOAD;
VAR
O : TObject;
CTX : TRttiContext;
TYP : TRttiType;
RTM : TRttiMethod;
OK : BOOLEAN;
BEGIN
O:=M.Data;
TRY
OK:=O IS TObject;
Result:=O.ClassName
EXCEPT
OK:=FALSE
END;
IF OK THEN BEGIN
CTX:=TRttiContext.Create;
TRY
TYP:=CTX.GetType(O.ClassType);
FOR RTM IN TYP.GetMethods DO
IF RTM.CodeAddress=M.Code THEN
EXIT(O.ClassName+'.'+RTM.Name)
FINALLY
CTX.Free
END
END;
Result:=IntToHex(NativeInt(M.Code),SizeOf(NativeInt)*2)
END;
FUNCTION MethodName(Event : TValidationFunc) : STRING; OVERLOAD; INLINE;
BEGIN
Result:=MethodName(TMethod(Event))
END;
Затем вам нужно только закодировать конкретное имя метода для каждого события, которое просто вызывает общую реализацию, и если вы пометите его как INLINE, есть большая вероятность, что оно даже не вызовет дополнительный вызов функции, а вместо этого вызовет его напрямую.
Кстати: на мой ответ сильно повлиял код, данный Cosmin Prund год назад в этом вопросе: RTTI информация для указателя метода
Если в вашем Delphi не определен NativeInt (не помню, когда именно они его реализовали), просто определите его как:
{$IFNDEF CPUX64 }
TYPE
NativeInt = INTEGER;
{$ENDIF }
person
HeartWare
schedule
22.02.2014