I need to get the name of the unit (namespace) of any TRttiType
.
so far, I have tried the following.
1) using the PTypeData.UnitName
, this solution works, but only when the TTypeKind is tkClass.
procedure ListAllUnits;
var
ctx : TRttiContext;
lType: TRttiType;
Units: TStrings;
begin
Units:=TStringList.Create;
try
ctx := TRttiContext.Create;
for lType in ctx.GetTypes do
if lType.IsInstance then //only works for classes
if Units.IndexOf(UTF8ToString(GetTypeData(lType.Handle).UnitName))<0 then
Units.Add(UTF8ToString(GetTypeData(lType.Handle).UnitName));
Writeln(Units.Text);
finally
Units.Free;
end;
end;
2) Parsing the QualifiedName
property, This solution works ok until now, but I'm not very happy with it.
procedure ListAllUnits2;
function GetUnitName(lType: TRttiType): string;
begin
Result := StringReplace(lType.QualifiedName, '.' + lType.Name, '',[rfReplaceAll])
end;
var
ctx: TRttiContext;
lType: TRttiType;
Units: TStrings;
begin
Units := TStringList.Create;
try
ctx := TRttiContext.Create;
for lType in ctx.GetTypes do
if Units.IndexOf(GetUnitName(lType)) < 0 then
Units.Add(GetUnitName(lType));
Writeln(Units.Text);
finally
Units.Free;
end;
end;
the question is, exist another reliable way to get the unit name of any TRttiType
?
It doesn't look like there is. The RTTI comes out of the TTypeData structure, which only has a UnitName field explicitly declared for specific types. (This predates D2010 and extended RTTI.) Your #2 looks like the best way to get it, and is probably how a hypothetical TRTTIObject.UnitName would calculate it if they were to put one in.
The information is there but Parsing the Qualified Name is currently the best way to get to it.
If you want to do it the hard way you can by:
In the system.pas unit you have a variable LibModuleList: PLibModule = nil;
that contains
the list of loaded modules. This is the pointer to the Raw RTTI Information that can be used without RTTI.pas. You could iterate all the raw information get determine the unit name.
The key values of the TLibModule are:
PLibModule = ^TLibModule;
TLibModule = record
Next: PLibModule; { Linked List of Loaded Modules)
Instance: LongWord;
...
TypeInfo: PPackageTypeInfo; { List of contained Package Information }
...
end;
Using the TypeInfo: PPackageTypeInfo;
you get access to
PPackageTypeInfo = ^TPackageTypeInfo;
TPackageTypeInfo = record
TypeCount: Integer;
TypeTable: PTypeTable;
UnitCount: Integer;
UnitNames: PShortString; { concatenation of Pascal strings,
one for each unit }
end;
Then there is TypeTable which contains the information to get to PTypeInfo.
sequence.
TTypeTable = array[0..MaxInt div SizeOf(Pointer) - 1] of Pointer;
PTypeTable = ^TTypeTable;
An example of how all this works can be found in Rtti.pas TPackage.MakeTypeLookupTable
is the key method. This method also shows that QualifiedName always will contain the UnitName. As such your original method of parsing QualfiedName can be depended on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With