Delphi 如何获取方法指针指向的方法的名称?

Delphi 如何获取方法指针指向的方法的名称?,delphi,delegates,function-pointers,rtti,Delphi,Delegates,Function Pointers,Rtti,我定义了以下内容: 如果验证正常或错误代码,则返回0的方法指针 TValidationFunc = Function(AParam: TAnObject): integer Of Object; 要执行的功能列表: Functions: TObjectList < TValidationFunc>; 如果该函数引发异常,如何在日志中获取原始函数的名称?一种方法是枚举可能的函数,以查找地址与目标匹配的函数。您可以从方法指针解码实例指针。从那里你可以得到类型。然后RTTI可以完成

我定义了以下内容:

  • 如果验证正常或错误代码,则返回0的方法指针

    TValidationFunc = Function(AParam: TAnObject): integer Of Object;
    
  • 要执行的功能列表:

    Functions: TObjectList < TValidationFunc>;
    

    如果该函数引发异常,如何在日志中获取原始函数的名称?

    一种方法是枚举可能的函数,以查找地址与目标匹配的函数。您可以从方法指针解码实例指针。从那里你可以得到类型。然后RTTI可以完成剩下的工作

    您可以考虑将名称以及方法指针存储在列表中。这将是一个简单易行的方法


    我的最后一个想法是,您可以在项目中包含可执行映射,并在那里查找函数。例如,如果您已经使用madExcept、EurekaLog或其他类似工具,那么这将非常简单

    最简单的解决方案是——正如David所暗示的那样——将名称与函数指针一起存储,如下所示:

    TYPE
      TValidationFunc = Function(AParam: TAnObject): integer Of Object;
      TFunctions = TDictionary<TValidationFunc,String>;
    
    VAR
      Functions : TFunctions;
    
    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;
    
    然后当你浏览它时:

    For valid In Functions.Keys Do Begin
      Try
        res := valid(MyObject);
      Except
        On E: Exception Do Begin
          Log('Error in function ' + Functions[valid] + ' : ' + E.Message, TNiveauLog.Error, 'PHVL');
          res := -1;
        End;
      End;
      Result := Result And (res = 0);
    End;
    
    通过这种方式,您可以将验证函数与TDictionary中的名称“链接”,这样,当您拥有一个验证函数时,您就可以获得另一个验证函数。

    好吧,永远不要说永不:-)。此函数将返回作为参数传递的事件的方法名称(格式为.ie.TMainForm.FormCreate)。不幸的是,您不能使用非类型化参数来允许传入任何类型的事件,但必须为您希望能够“解码”的每个方法签名编写特定的例程:

    像这样使用它:

    TYPE
      TValidationFunc = Function(AParam: TAnObject): integer Of Object;
      TFunctions = TDictionary<TValidationFunc,String>;
    
    VAR
      Functions : TFunctions;
    
    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试过了

    有一点需要注意:这只适用于已生成RTTI的方法,并且仅适用于Delphi 2010及更高版本(在Delphi 2010及更高版本中,RTTI大量增加了可用数据量)。因此,为了确保它能够工作,您应该将要跟踪的方法放在已发布的部分中,因为这些方法(默认情况下)总是会生成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;
    
    然后,您只需要为每个简单调用常规实现的事件编写一个特定的MethodName,如果将其标记为内联,则很有可能它甚至不会引发额外的函数调用,而是直接调用它

    顺便说一句:我的回答受到Cosmin一年前在这个问题上给出的代码的严重影响:

    如果您的Delphi没有定义NativeInt(不记得具体是什么时候实现的),只需将其定义为:

    {$IFNDEF CPUX64 }
    TYPE
      NativeInt = INTEGER;
    {$ENDIF }
    

    为了简化我的示例,我说我将函数存储在列表中,但实际上我的函数在一个类中,因此我可以将函数名存储在我的类中。显然,您在列表中存储的是一个方法指针,而不是一个类。这就是代码所说的。但是你确实可以解码方法指针并获得一个类。我不能增加你答案的分数(这是你名声不好时得到的;),但是非常感谢你快速而好的答案,大卫。欢迎你。我相信你很快就会得到一些选票。我相信其他人会给出更多的答案。谢谢你的答案和代码示例。关键是当我输入Functions.Add(Routine1,'Routine1');我脑子里有个小声音说:老兄,你错过了什么……但是嘿!:-)看看我的另一个答案——这也许是你问题的解决方案?谁说从不?这是我答案的第一段,充实成代码。“谁说从不?”我说了…:-)非常感谢你,HeartWare,这正是我想要的!!!当我看到解决方案时,我同时认为“它没有那么复杂”和“我可能永远也找不到它”。@o.schwab:记住接受你认为对你帮助最大的答案作为解决方案。
    {$IFNDEF CPUX64 }
    TYPE
      NativeInt = INTEGER;
    {$ENDIF }