Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi mit string an PTypeinfo rankommen (https://www.delphipraxis.net/68287-mit-string-ptypeinfo-rankommen.html)

_frank_ 27. Apr 2006 13:36


mit string an PTypeinfo rankommen
 
hallo,
vor dem Kompilieren kann man sich die Typinfos (z.B. um die werte eines Enums zu bekommen) per TypeInfo holen, dieses funktioniert aber nicht zur Laufzeit. Ich brauche dies aber dynamisch zur Laufzeit.
Momentan fülle ich ein array of PTypeinfo aller mir bekannten typen per Typeinfo.

gibt es eine möglichkeit per typ-string an diesen Pointer zu kommen?

Gruß Frank

generic 27. Apr 2006 13:46

Re: mit string an PTypeinfo rankommen
 
es kann nicht funktionieren, da die typinfos erst beim kompilieren erzeugt werden.

du kannst versuchen über die ide interfaces an den source/parser zu kommen und das dann auslesen.
schliesslich tut das ja auch der code-explorer

negaH 27. Apr 2006 14:11

Re: mit string an PTypeinfo rankommen
 
Damit http://www.delphipraxis.net/internal...065&highlight= funktioniert das.

Gruß Hagen

_frank_ 27. Apr 2006 14:21

Re: mit string an PTypeinfo rankommen
 
schaue grade ob ich die unter D3 zum Laufen bekomme, das es keine dynamischen arrays, default-werte, overload etc gibt...

negaH 27. Apr 2006 14:38

Re: mit string an PTypeinfo rankommen
 
Uff, das ist im Falle dieser Unit aber schlecht.

Extrahiere dir meine Funktion EnumTypeInfos(). Diese enthält den entscheidenden Trick und der Rest ist nur ein komfortabler Überbau -> Hilfsfunktonen.

Auf alle Fälle kannst du nun über alle RTTI Records der Module iterieren. Mit PTypeInfo^.Name kannst du dabei einen Vergleich mit deinem Suchstring anstellen und findest auf diese Weise den PTypeInfo Zeiger zu deinem Namen einer TypInfo.

Beachte dabei das es sehr wohl zulässig ist zwei unterschiedliche Typen mit gleichem Namen aber in unterschiedlichen Units zu deklarieren. Dh. der reine Name eines Types ist nicht eineindeutig !! Erst die Verknüfpung von Modulname + Unitname + Typname ist eineindeutig.

Gruß Hagen

generic 27. Apr 2006 14:43

Re: mit string an PTypeinfo rankommen
 
http://www.instantobjects.org/

haben ein ide plugin welches das über die toolsapi löst.
bin auf die schneller aber nicht dahinter gekommen welches interface genutzt wird.

[edits]
vergesst mal meine posts, ich bin im büro halbschlaf.

_frank_ 27. Apr 2006 14:49

Re: mit string an PTypeinfo rankommen
 
Mit der normalen RTTI ist das wohl nicht möglich?

habs mal so probiert:

Delphi-Quellcode:
//longword => integer

function FindTypeInfo(const ATypeName: string): PTypeInfo;

type
  TEnumTypeInfoFunc = function(AUserData: Pointer; ATypeInfo: PTypeInfo): Boolean; register;

  //findtypeinfo

function EnumTypeInfos_base(AModule: integer; AFunc: TEnumTypeInfoFunc; AUserData: Pointer): PTypeInfo;
// copyright (c) 1998 Hagen Reddmann

  function GetBaseOfCode(AModule: integer; var ACodeStart, ACodeEnd: PChar): Boolean; register;
  // get Codesegment pointers, check if module is a valid PE
  asm
           PUSH EDI
           PUSH ESI
           AND  EAX,not 3
           JZ   @@2
           CMP  Word Ptr [EAX],'ZM';
           JNE  @@1
           MOV  ESI,[EAX + 03Ch]
           CMP  Word Ptr [ESI + EAX],'EP'
           JNE  @@1
           MOV  EDI,[EAX + ESI + 014h + 008h]
           ADD  EAX,[EAX + ESI + 014h + 018h]
           ADD  EDI,EAX
           MOV  [EDX],EAX
           MOV  [ECX],EDI
           XOR  EAX,EAX
    @@1:  SETE AL
    @@2:  POP  ESI
           POP  EDI
  end;

type
  PLongWord = ^integer;
  PByte = ^Byte;
var
  P,E,K,N: PChar;
  L: Integer;
begin
  Result := nil;
  try
    if GetBaseOfCode(AModule, P, E) then
      while P < E do
      begin
        integer(P) := integer(P) and not 3;
        K := P + 4;
        if (PLongWord(P)^ = integer(K)) and (TTypeKind(K^) >= Low(TTypeKind)) and (TTypeKind(K^) <= High(TTypeKind)) then
        begin
          L := PByte(K + 1)^; // length Info.Name
          N := K + 2;         // @Info.Name[1]
          if (L > 0) and (N^ in ['_', 'a'..'z', 'A'..'Z']) then // valid ident ??
          begin
            repeat
              Inc(N);
              Dec(L);
            until (L = 0) or not (N^ in ['_', 'a'..'z', 'A'..'Z', '0'..'9']);
            if L = 0 then // length and ident valid
              if not Assigned(AFunc) or AFunc(AUserData, Pointer(K)) then // tell it and if needed abort iteration
              begin
                Result := Pointer(K);
                Exit;
              end else K := N;
          end;
        end;
        P := K;
      end;
  except
  end;
end;

function EnumTypeInfos(AFunc: TEnumTypeInfoFunc; AUserData: Pointer): PTypeInfo;
type
  PModulesEnumData = ^TModulesEnumData;
  TModulesEnumData = packed record
    AFunc: TEnumTypeInfoFunc;
    AUserData: Pointer;
    AResult: PTypeInfo;
  end;

  function EnumTypeInfosInModule(AModule: integer; AData: PModulesEnumData): Boolean; register;
  begin
    with AData^ do
    begin
      AResult := EnumTypeInfos_base(AModule, AFunc, AUserData);
      Result := AResult = nil;
    end;
  end;

var
  Data: TModulesEnumData;
begin
  Data.AFunc := AFunc;
  Data.AUserData := AUserData;
  Data.AResult := nil;
  EnumModules(TEnumModuleFunc(@EnumTypeInfosInModule), @Data);
  Result := Data.AResult;
end;

function IsTypeCorrespondingToName(AName: Pointer; ATypeInfo: PTypeInfo): Boolean; register;
begin
  Result := AnsiCompareText(PChar(AName), ATypeInfo.Name) = 0;
end;

function FindTypeInfo(const ATypeName: string): PTypeInfo;
begin
  Result := EnumTypeInfos(IsTypeCorrespondingToName, PChar(ATypeName));
end;
leider ist z.B. FindTypeInfo('TBorderIcon') = nil
sollte abr nicht so sein ;)

Gruß Frank

negaH 27. Apr 2006 16:29

Re: mit string an PTypeinfo rankommen
 
Ich kann beim Überfliegen deines Source jetzt keinen Fehler entdecken. Du solltest die HTML Tags hier benutzen damit dein Source besser lesbar ist. Einfach im Editor oben den Button "Delphi-Code" drücken.

Kann es sein das der Typ "TBorderIcons" heist ?

Zitat:

Mit der normalen RTTI ist das wohl nicht möglich?
Nein. Nach persönlicher Aussage einiger Borland Programmierer in den Newsgroups mir gegenüber, ist es definitiv überhaupt nicht möglich was mein Code da macht. So kann man sich irren ;)

Gruß Hagen

_frank_ 27. Apr 2006 16:38

Re: mit string an PTypeinfo rankommen
 
ich hab im gegensatz zu deinem source Longword durch integer (32bit) ersetzt, das PLongword als ^Integer definiert und die ursprungsfunktion _base genannt, da es kein overload gibt.

wegen dem Bordericon (definition von TForm.Bordericons):
Delphi-Quellcode:
type
TBorderIcon = (biSystemMenu, biMinimize, biMaximize, biHelp);
TBorderIcons = set of TBorderIcon;
somit müsste TBordericon funktionieren (hab auch +s probiert, das gleiche Resultat)

Gruß Frank

negaH 27. Apr 2006 17:16

Re: mit string an PTypeinfo rankommen
 
Ich habe deinen Source 1 zu 1 nun mal in Delphi 5 und Delphi 3 getestet. Jeweils ohne und mit Packages. Es funktionierte immer egal ob ich TBorderIcon oder TBorderIcons benutze.

Es sieht wohl so aus das du Unit Forms.pas garnicht in deinem Projekt benutzt und der Compiler somit auch nicht den Typ TBorderIcon einlinkt.

Obige Methode kann ja nur RTTIs finden die auch im Program verwendet werden !!

Am besten kompilierst du dein Projekt mal mit Packages so das Package VCL30.dpl auch eingelinkt wird. Dann müsste deine obige Funktion auch diese RTTI finden da nun der Compiler/Linker diesen Typ nicht mehr aus dem VCL30 Package "weg-linken" kann ;)

Mit GetModuleFindName(FindHInstance(PTypeInfo), xzy); kannst du dann sehen das diese RTTI im Codesegment des Packages VCL30 abgelegt ist.

Gruß Hagen

[edit]
Und nochwas !! Kann es sein das du diese Funktion in eine DLL augelagert hast ? Das kann dann nicht funktioinieren da dann nur die RTTI innerhalb dieser DLL iteriert wird. Du müsstest das Modul-Handle -> HInstance des Prozesses dazu haben und an EnumTypeInfo() übergeben. Bei einer DLL die Packages benutzt und die Hauptanwendung benutzt die gleichen Packages wird EnumModules() innerhab der DLL auf die gleichen globalen Datenstrukturen zugreifen wie der Hauptprozess. Dh. in diesem Moment hat diese DLL auch Zugriff auf die Liste aller geladenen Module im Prozess und kann somit auch alle RTTIs finden.
Falls du NICHT mit Packages arbeiten möchtest aber denoch eine DLL benutzen willst so kann ich dir einen weiteren Trick geben der denoch alle Module des Prozesses itererien kann.
[/edit]

_frank_ 27. Apr 2006 17:43

Re: mit string an PTypeinfo rankommen
 
mhm...hatte es vorhin in nem fertigen Programm als zusatzmodul probiert...keine DLL, nur ein button und ne editbox ;)

habs mal in ne neue Anwendung rein, da gehts...ma sehen, worans lag...

hab noch nicht mit Packages gearbeitet (denk ich).

gibt es noch eine enumeration der registrierten klassen?

definiere massig klassen per registerClasses um Sie per getClass zu finden. Momentan hab ich noch eine Separate Stringlist, um diese Klassen (TComponent) in eine Combobox zu bekommen.

Gruß Frank

negaH 27. Apr 2006 18:14

Re: mit string an PTypeinfo rankommen
 
Zitat:

gibt es noch eine enumeration der registrierten klassen?
Du hast es schon gesagt: RegisterClass() und GetClass() sind die Wege die die Borlandianer vorgesehen haben. Allerdings hast du natürlich mit EnumTypeInfo() nun Zugriff auf alle RTTI aller geladenen Module und ergo auch auf alle Klassen die in der Anwednung verwendet werden. Also nicht nur diejenigen die mit RegisterClass() registriert worden, sondern als ALLE. Auch diejenigen die private sind und in einer normalen Anwendung garnicht Unitübergreifend angesprochen werden können.

Eine RTTI wird erzeugt für JEDEN deklarierten Typ der im Delphi Source deklariert wurde und auch duch die Anwednung tatsälchich benutzt wurde. Das umfast Sets, Mengen, ordinale Typen wie Char/Integer, komplexe Typen with TMethod, TClass, TObject, Komponenten und sogar Interfaces.

Die einzisgte Ausnahme wären lokale Typen die lokal in einer Prozedur/Funktion deklariert wurden.

Mit EnumTypeInfo() kannst du mit ATypeInfo.Kind = tkClass und mit nach folgendem GetTypData(ATypeInfo).ClassType die TClass einer TypInfo in Erfahrung bringen. Du kannst also EnumTypeInfo() so umbauen das sie das gleiche wie GetClass() macht aber OHNE mit RegisterClass() und eigener Liste zu arbeiten.

Allerdings, aus Gründen der Portierbarkeit und Anwendungssicherheit würde ich eben eine eigene RegisterClass() Funktionalität der EnumTypeInfo() vorziehen. Eben weil EnumTypeInfo() auf ein Verhalten des Compilers/Linkers beruht das nicht nur undokumentiert ist sondern sogar Borland unbekannt scheint. Das heist aber nun auch nicht das man EnumTypeInfo() verteufeln sollte ;) denn nach meinen Erfahrungen hat sich seit Delphi 2 rein garnischts an der Funktion von EnumTypeInfo() verändert. Heist also EnumTypeInfo() läuft sehr stabil mit allen Delphi/BCB Versionen und wird es meiner Meinung nach auch so lange beiben bis wir einen komplett neuen Compiler/Linker samt VCL erleben. Die Veränderungen der RTTI im Compiler dürften so gravierend sein das es ein komplett neuer Compiler wäre. Immerhin sind die RTTIs quasi Übereste von Datenstrukturen die der Compiler selber während des Compilierungsprozeses benötiggt und erzeugt. Ähnliche, ja fast sogar identische, Datenstrukturen wird man in DCU und den DSM Dateien wiederfinden. Übertragen findet man dies auch bei den Packages und den DCP Dateien.

Gruß Hagen

_frank_ 27. Apr 2006 18:23

Re: mit string an PTypeinfo rankommen
 
ähm, ich glaube, du hast mich falsch verstanden.
ich brauche ne liste der Komponenten, die registriert sind und keine Unterklassen/Strukturen. die enum-funktionen brauchen aber alle ein PTypeInfo, aber ich brauche die oberste Ebene. es soll also folgendes herauskommen:

TButton
TEdit
TImagelist
TMemo
...

naja, umbasteln ist so ne Sache, ich nutze die rtti nur, aber so richtig tiefgründig verstehen tu ich sie nicht (ist mehr nachschauen, welche typen gebraucht werden und probieren) ;>
die Datensegmentpatches, die dahinterstehen sind mir ein Rätsel.

Gruß Frank

negaH 27. Apr 2006 22:07

Re: mit string an PTypeinfo rankommen
 
An die interne Liste die in RegisterClass() benutzt wird kommst du nicht in jeder Delphi Version, soviel ich weis.
Du kannst im grunde nur mit RegisterClass() Klassen dort hinzufügen und mit GetClass() nachfragen ob eine Klasse registriert wurde. Man könnte per Tricks an diese globale Variable herankommen, das geht mit absoluter Sicherheit. Das wäre dann aber von der Delphi Version abhängig.

Allerdings was brauchst du ?
So wie ich dich zwischen den Zeilen verstanden habe suchst du "TopLevel" Komponenten, also die Komponenten die keine weiteren Descands besitzen. Auch das lässt sich mit EnumTypeInfo() bewerkstelligen.

Als erstes mit EnumTypeInfo() eine Liste aller Klassen erzeugen die von TComponent abstammen. Danach müssen aus dieser Liste alle Klassen rausgefiltert werden die selber eine Vorfahrklasse der gefundenen Klassen in dieser List sind.

Zb. wird diese Liste auch TEdit und TCustomEdit enthalten. TEdit ist gesucht und ein Nachfahre von TCustomEdit. TCustomEdit ist eine abstrakte Vorfahrklasse und besitzt TEdit als Nachfahre in der Liste. Also wird TCustomEdit durch Ausschlußverfahren über TEdit aus der Liste rausgeschmissen.
Übrig bleibt eine Liste alle Klassen der Anwendung die abgeleitet sind von TComponent und keine Nachfahren besitzen.

Fragt sich nur was du damit weiter anfangen möchtest. Denn die Hauptaufgabe von RegisterClasses() und GetClass() ist einzigst das Mapping einen Klassennamens als String in eine TClass = Zeiger auf die VMT einer Klasse und indirekt ein Zeiger auf die RTTI. D.h. die reine Funktionalität von RegisterClasses(), GetClass() kannst du 1 zu 1 auch mit EnumTypeInfo() erreichen, ohne Speicherlisten etc.pp.

Gruß Hagen


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:57 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz