Delphi RTTI 对象检查器
Delphi RTTI Object Inspector
我正在尝试为我正在编写的绘图应用构建一个简化的对象检查器。
我正在尝试为所选对象及其子对象动态获取 RTTI。如果给定的 属性 是 class (tkClass),我想递归调用 GetRTTIObject,将 属性 作为对象来获取它的“子属性”(即 BaseObj.Brush.Color 或 BaseObj.Pen.Width 等)。我怀疑我想传递那个对象的实例,当有人指出它是什么时,它会非常明显。如何让实例传递给我的函数?或者我应该查看 TRttiInstance 的属性 classes....?
我知道它在“级别 0”下工作,因为我可以将 BaseObject.Brush 传递到我第一次调用 GetRTTIObject 并获得 TBrush 属性列表。如何递归向下钻取?
我似乎得到了某种指针 Value := GetPropValue(AObj, Prop.Name);
我是否以某种方式取消引用以获取我的实例...?
此致,
罗布
简化测试class定义:
TBaseClass = class(TObject)
private
FFont: TFont;
FBrush: TBrush;
FPen: TPen;
FCaption: String;
FFloat1: Real;
FInt1: Integer;
published
property Font: TFont Read FFont Write FFont;
property Brush: TBrush Read FBrush Write FBrush;
property Pen: TPen Read FPen Write FPen;
property Caption: String Read FCaption Write FCaption;
property Float1: Real Read FFloat1 Write FFloat1;
property Int1: Integer Read FInt1 Write FInt1;
end;
我的 RTTI 程序是:
procedure TfrmMain.GetRTTIClass(AClass: TClass; Items: TStrings; Indent: Integer);
var
LContext: TRttiContext;
LType: TRttiType;
Prop: TRttiProperty;
PropString: String;
PropInfo: PPropInfo;
Tabs: String;
I: Integer;
Value: Variant;
begin
LContext := TRttiContext.Create();
try
for I := 0 to Indent do
Tabs := Tabs + ' '; //chr(9)
Log(Format('Get RTTI (Class) for "%s"', [AClass.ClassName]));
LType := LContext.GetType(AClass.ClassInfo);
Items.Add(Tabs + 'RTTI for: ' + Ltype.Name);
Items.Add(Tabs + 'Package Name: ' + LType.Package.Name);
Items.Add(Tabs + '-- Properties --');
for Prop in LType.GetProperties do
begin
PropString := 'property: ' + Prop.Name;
PropInfo := GetPropInfo(AClass, Prop.Name);
PropString := PropString + ': ' + GetEnumName(TypeInfo(TTypeKind), Ord(Prop.PropertyType.TypeKind));
if propInfo <> nil then begin
PropString := PropString + ': ' + PropInfo^.PropType^.Name;
case propInfo.PropType^.Kind of
tkClass: begin
PropString := PropString + ' (Class)' ; // ' GetProp Value: ' + IntToHex(PropInfo.GetProc, 8); // Items.Add('--- Get RTTI ---');(Class)';
Log(Format('GetRTTI: %s (%s)', [Prop.Name, PropInfo^.PropType^.Name]));
// TODO: Get a reference to the object and call GetRTTI
// TODO: Or change function to work from classtype rather than object
// GetRTTIObject(### WHAT GOES HERE?!?!?, Items, Indent + 1);// := PropString + ' Class';
end;
end;
end;
Items.Add(Tabs + PropString);
end;
finally
LContext.Free;
end;
end;
哎呀!!
我看到我放错了函数.....有问题的那个接受了一个 TObject 并且赋值是:
LType := LContext.GetType((AObject.ClassInfo); (AObject.ClassType 似乎也有效...)...
刚才不在我的开发站,但认为其他一切都一样....
你的例子中的问题是 TBrash 有 属性 TBitMap,TBitMap 有 TCanvas,TCanvas 有 TBrash。函数 GetRTTIClass 的调用将无限递归。但是,如果为每个 class 设置一次仅获取 RTTI 的条件,则可以修复您的功能。
uses System.Generics.Collections;
var ListClasses:TList<TClass>;
LContext : TRttiContext;
implementation
procedure TfrmMain.FormCreate(Sender: TObject);
begin
LContext := TRttiContext.Create();
ListClasses:=TList<TClass>.Create;
end;
procedure TfrmMain.GetRTTIClass(AClass: TClass; Items: TStrings; Indent: Integer);
var
LType: TRttiType;
Prop: TRttiProperty;
PropString: String;
Tabs: String;
I: Integer;
begin
if ListPrinted.Contains(AClass) then Exit
else ListPrinted.Add(AClass);
for I := 0 to Indent do Tabs := Tabs + ' ';
LType := LContext.GetType(AClass.ClassInfo);
Items.Add(Tabs + 'RTTI for: ' + Ltype.Name);
Items.Add(Tabs + 'Package Name: ' + LType.Package.Name);
Items.Add(Tabs + '-- Properties --');
for Prop in LType.GetProperties do begin
PropString := 'property: ' + Prop.Name;
PropString := PropString + ': ' + GetEnumName(TypeInfo(TTypeKind), Ord(Prop.PropertyType.TypeKind))+' '+Prop.PropertyType.Name;
Items.Add(Tabs + PropString);
case Prop.PropertyType.Handle^.Kind of
tkClass: begin
GetRTTIClass(Prop.PropertyType.Handle^.TypeData^.ClassType, Items,Indent+2);
end;
end;
end;
procedure TfrmMain.btn1Click(Sender: TObject);
begin
GetRTTIClass(TBaseClass, Items,0);
end;
好的,我对程序做了一些修改。解析 class 还不够。我需要实例句柄。
要递归调用我的过程(以对象而不是 class 作为第一个参数的过程),我需要子对象的实例(例如 AObj.Font) .我可以通过以下方式获得它:
case Prop.PropertyType.TypeKind of
tkClass: begin
SubObj := GetObjectProp(AObj, Prop.Name);
GetRTTIObject2(SubObj, Tree, ChildNode, Indent + 2);
end;
end;
真的很简单,一旦我全神贯注。
仍然会投票给另一个答案作为解决方案,因为它为避免另一个陷阱提供了很好的指导。 :)
我正在尝试为我正在编写的绘图应用构建一个简化的对象检查器。
我正在尝试为所选对象及其子对象动态获取 RTTI。如果给定的 属性 是 class (tkClass),我想递归调用 GetRTTIObject,将 属性 作为对象来获取它的“子属性”(即 BaseObj.Brush.Color 或 BaseObj.Pen.Width 等)。我怀疑我想传递那个对象的实例,当有人指出它是什么时,它会非常明显。如何让实例传递给我的函数?或者我应该查看 TRttiInstance 的属性 classes....?
我知道它在“级别 0”下工作,因为我可以将 BaseObject.Brush 传递到我第一次调用 GetRTTIObject 并获得 TBrush 属性列表。如何递归向下钻取?
我似乎得到了某种指针 Value := GetPropValue(AObj, Prop.Name);
我是否以某种方式取消引用以获取我的实例...?
此致, 罗布
简化测试class定义:
TBaseClass = class(TObject)
private
FFont: TFont;
FBrush: TBrush;
FPen: TPen;
FCaption: String;
FFloat1: Real;
FInt1: Integer;
published
property Font: TFont Read FFont Write FFont;
property Brush: TBrush Read FBrush Write FBrush;
property Pen: TPen Read FPen Write FPen;
property Caption: String Read FCaption Write FCaption;
property Float1: Real Read FFloat1 Write FFloat1;
property Int1: Integer Read FInt1 Write FInt1;
end;
我的 RTTI 程序是:
procedure TfrmMain.GetRTTIClass(AClass: TClass; Items: TStrings; Indent: Integer);
var
LContext: TRttiContext;
LType: TRttiType;
Prop: TRttiProperty;
PropString: String;
PropInfo: PPropInfo;
Tabs: String;
I: Integer;
Value: Variant;
begin
LContext := TRttiContext.Create();
try
for I := 0 to Indent do
Tabs := Tabs + ' '; //chr(9)
Log(Format('Get RTTI (Class) for "%s"', [AClass.ClassName]));
LType := LContext.GetType(AClass.ClassInfo);
Items.Add(Tabs + 'RTTI for: ' + Ltype.Name);
Items.Add(Tabs + 'Package Name: ' + LType.Package.Name);
Items.Add(Tabs + '-- Properties --');
for Prop in LType.GetProperties do
begin
PropString := 'property: ' + Prop.Name;
PropInfo := GetPropInfo(AClass, Prop.Name);
PropString := PropString + ': ' + GetEnumName(TypeInfo(TTypeKind), Ord(Prop.PropertyType.TypeKind));
if propInfo <> nil then begin
PropString := PropString + ': ' + PropInfo^.PropType^.Name;
case propInfo.PropType^.Kind of
tkClass: begin
PropString := PropString + ' (Class)' ; // ' GetProp Value: ' + IntToHex(PropInfo.GetProc, 8); // Items.Add('--- Get RTTI ---');(Class)';
Log(Format('GetRTTI: %s (%s)', [Prop.Name, PropInfo^.PropType^.Name]));
// TODO: Get a reference to the object and call GetRTTI
// TODO: Or change function to work from classtype rather than object
// GetRTTIObject(### WHAT GOES HERE?!?!?, Items, Indent + 1);// := PropString + ' Class';
end;
end;
end;
Items.Add(Tabs + PropString);
end;
finally
LContext.Free;
end;
end;
哎呀!!
我看到我放错了函数.....有问题的那个接受了一个 TObject 并且赋值是:
LType := LContext.GetType((AObject.ClassInfo); (AObject.ClassType 似乎也有效...)...
刚才不在我的开发站,但认为其他一切都一样....
你的例子中的问题是 TBrash 有 属性 TBitMap,TBitMap 有 TCanvas,TCanvas 有 TBrash。函数 GetRTTIClass 的调用将无限递归。但是,如果为每个 class 设置一次仅获取 RTTI 的条件,则可以修复您的功能。
uses System.Generics.Collections;
var ListClasses:TList<TClass>;
LContext : TRttiContext;
implementation
procedure TfrmMain.FormCreate(Sender: TObject);
begin
LContext := TRttiContext.Create();
ListClasses:=TList<TClass>.Create;
end;
procedure TfrmMain.GetRTTIClass(AClass: TClass; Items: TStrings; Indent: Integer);
var
LType: TRttiType;
Prop: TRttiProperty;
PropString: String;
Tabs: String;
I: Integer;
begin
if ListPrinted.Contains(AClass) then Exit
else ListPrinted.Add(AClass);
for I := 0 to Indent do Tabs := Tabs + ' ';
LType := LContext.GetType(AClass.ClassInfo);
Items.Add(Tabs + 'RTTI for: ' + Ltype.Name);
Items.Add(Tabs + 'Package Name: ' + LType.Package.Name);
Items.Add(Tabs + '-- Properties --');
for Prop in LType.GetProperties do begin
PropString := 'property: ' + Prop.Name;
PropString := PropString + ': ' + GetEnumName(TypeInfo(TTypeKind), Ord(Prop.PropertyType.TypeKind))+' '+Prop.PropertyType.Name;
Items.Add(Tabs + PropString);
case Prop.PropertyType.Handle^.Kind of
tkClass: begin
GetRTTIClass(Prop.PropertyType.Handle^.TypeData^.ClassType, Items,Indent+2);
end;
end;
end;
procedure TfrmMain.btn1Click(Sender: TObject);
begin
GetRTTIClass(TBaseClass, Items,0);
end;
好的,我对程序做了一些修改。解析 class 还不够。我需要实例句柄。
要递归调用我的过程(以对象而不是 class 作为第一个参数的过程),我需要子对象的实例(例如 AObj.Font) .我可以通过以下方式获得它:
case Prop.PropertyType.TypeKind of
tkClass: begin
SubObj := GetObjectProp(AObj, Prop.Name);
GetRTTIObject2(SubObj, Tree, ChildNode, Indent + 2);
end;
end;
真的很简单,一旦我全神贯注。
仍然会投票给另一个答案作为解决方案,因为它为避免另一个陷阱提供了很好的指导。 :)