Delphi 如果我只有类/VMT地址,如何获取类的单元名称

Delphi 如果我只有类/VMT地址,如何获取类的单元名称,delphi,Delphi,正如我读到的 VMT还包含许多“神奇”字段以支持功能 例如父类链接、实例大小、类名、动态方法 表,发布方法表,发布字段表,RTTI表, magic fields的初始化表,不推荐使用的OLE自动化 调度表和实现接口表 看起来VMT没有包含包含定义类的单元名称的字段。是否涉及到一些“编译器魔法”?我不明白为什么这里要涉及VMT。TObject已经为此公开了一个类函数UnitName VMT包括一个指向类RTTI的指针(由ClassInfo方法提供);类RTTI包括一个类单元名称。作为一个练习,您可

正如我读到的

VMT还包含许多“神奇”字段以支持功能 例如父类链接、实例大小、类名、动态方法 表,发布方法表,发布字段表,RTTI表, magic fields的初始化表,不推荐使用的OLE自动化 调度表和实现接口表


看起来VMT没有包含包含定义类的单元名称的字段。是否涉及到一些“编译器魔法”?

我不明白为什么这里要涉及VMT。TObject已经为此公开了一个
类函数UnitName


VMT包括一个指向类RTTI的指针(由
ClassInfo
方法提供);类RTTI包括一个类单元名称。作为一个练习,您可以从VMT指针获取单元名称,我编写了以下内容(在Delphi XE上测试):

使用TypInfo;
类型
TObj=类
结束;
程序TForm1.按钮3点击(发送方:TObject);
变量
Obj:TObj;//虚拟obj实例
VMT:指针;
P:指针;//班级信息
开始
//您可以这样获得VMT指针
对象:=TObj.Create;
VMT:=PPointer(Obj)^;
对象自由;
//大概
VMT:=指针(TObj);
P:=PPointer(PByte(VMT)+vmtTypeInfo)^;
如果P为零,那么
ShowMessage(GetTypeData(P).UnitName);
结束;

请注意,必须测试ClassInfo的nil。例如,SysUtils.ERangeError没有它。

对于D2009之前的版本,可以使用
typinfo.GetTypeData(TSomeClass.ClassInfo).UnitName
既然您提到,RTTI指针可以从
ClassInfo
方法获得,为什么您要演示这样一种迂回的方法来获得相同的信息?您可以简单地用
P:=TObj.ClassInfo
@RobKennedy替换所有代码-您不能直接调用
ClassInfo
方法,而只能使用指向未知类的VMT的指针。您可以尝试以某种方式键入VMT指针,但我没有找到如何执行该操作。@Serg只是将VMT指针投射到
TClass
。然后,您可以创建一个一行代码:
TypInfo.GetTypeData(TClass(vmt).ClassInfo).UnitName
,但是您首先是如何以及为什么获得指向vmt的原始指针的呢?直接使用类引用或对象引用即可。你甚至不需要任何关于“VMT”概念的知识。
uses TypInfo;

type
  TObj = class

  end;

procedure TForm1.Button3Click(Sender: TObject);
var
  Obj: TObj;    //  dummy obj instance
  VMT: Pointer;
  P: Pointer;   // class info

begin
// you can get VMT pointer so
  Obj:= TObj.Create;
  VMT:= PPointer(Obj)^;
  Obj.Free;
// or so
  VMT:= Pointer(TObj);

  P:= PPointer(PByte(VMT) + vmtTypeInfo)^;
  if P <> nil then
    ShowMessage(GetTypeData(P).UnitName);
end;
procedure MessageException(E: Exception);
var
  TI: TypInfo.PTypeInfo;
begin
  TI := E.ClassInfo;
  if Assigned(TI) then
  begin
    Dialogs.MessageDlg(TypInfo.GetTypeData(TI).UnitName + '.' +
      E.ClassName + ': ' + E.Message, Dialogs.mtError, [Dialogs.mbOK], 0, Dialogs.mbOK);
  end
  else
  begin
    Dialogs.MessageDlg(E.ClassName + ': ' + E.Message, Dialogs.mtError, [Dialogs.mbOK], 0, Dialogs.mbOK);
  end;
end;