Einzelnen Beitrag anzeigen

Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#15

Re: Verwenden von GetClass und RegisterClass

  Alt 24. Sep 2004, 14:51
Naja, das eigentliche Problem entsteht woanders. Jede eigene TForm Klasse enthält in deren PUBLISHED Sektion Felder auf deren Komponenten und Controls. Der Compiler legt also in der RTTI im Codesegment zu dieser TForm Klasse auch die Informationen auf diese Felder ab. Diese Informationen enthalten den Namen der Komponente/Controls und auch den Klassentyp. Dieser Klassentyp ist aber nichts anderes als ein Zeiger in ds Codesegment wo dann wiederum die RTTI dieser Klassen zu finden ist.

So, lädt sich nun ein TForm selber aus deiner DFM so ist dieser TForm-Klasse sehr wohl bekannt welche Komponenten/Controls es selber enthält und welchen Klassentyp diese besitzen. Dazu muß die virtuelle DFM-Laderoutine -> eg. das Delphi VCL Streaming System, nur ausgehend von der aktuellen TForm Klasse in deren RTTI nachschlagen.

Lädt man aber eine x'beliebige DFM, ohne das die dazugehörige TForm-Klasse erzeugt wurde so funktioniert dieses nicht mehr. Da nun eben kein Zugriff mehr auf die korrekte RTTI und deren published Komponentenfelder mehr vorhanden ist. Somit entsteht ein Fehler das eine bestimmte Klasse nicht registriert sei. Denn, eine Klasse ist nichts anderes wie ein Zeiger in das Codesegment. Solche Zeiger kann man aber in DFM's nicht direkt speichern. Um das denoch hinzubekommen benutzt das Delphi Streaming System den Feld-Namen und Klassentyp und speichert diese Informationen in der DFM. Sprich in der DFM steht "Object" -> "Button1" : "TButton" als Strings. Object bezeichnet ein Token so das das Streamingsystem weis das jetzt ein TObject == Klasse gemeint ist. "Button1" wiederum ist der Name des Feldes das in der TForm Klasse definiert wurde -> also der Name der Komponenten. Und "TButton" ist die eigentliche Klasse der Komponente. Delphis Streamingsystem lädt diese Strings aus der DFM und versucht nun diese Namenstrings in Klassen-Zeiger umzuwandeln. Natürlich muß es dazu eben den String "TButton" irgendwo nachschlagen. Da Delphi selber NICHT so einen Trick wie den obigen benutzt (geht ja offiziell auch nicht laut Borland), benutzt die RTL zwei Wege. Zuerstmal schaut es in der aktuellen Instance==Object in deren RTTI nach ob es ein Feld mit Namen "Button1" gibt. Wenn ja vergleicht es über die RTTI dessen Klassennamen "TButton" mit dem der aus der DFM geladen wurde. Wenn diese übereinstimmen hat das Streamingsystem schon die richtige und eindeutige Zuordnung gefunden. Wenn nein, geht es iterariv über die weiteren Felder der Instance und deren RTTI drüber und sucht.

Wenn all dies fehlschlägt greift nun das dynamsiche Klassen-Registratuons-Konzept. Mit RegisterClass() wird eine Klasse in einer globalen TList gespeichert und registeriert. Delphi geht nun diese Liste iterativ durch und vergleich von den darin gespeicherten Klassen == RTTI's die Klassennamen mit "TButton". Sollte ein übereinstimmerneder Eintrag gefunden worden sein so hat man zum String "TButton" die Klasse == Zeiger in das Codesegment zur RTTI dieser TButton Klasse gefunden.

Statt nun, wie du oben gezeigt hast, mit RegisterClass() alle im Code enthaltenen Klassen zu registrieren wäre es konzeptionell am saubersten die DFM nur durch die richtige TForm Klasse laden zu lassen. Sollte dies aber nicht möglich sein so stellt Delphis Komponmenten-Streaming-Sytsem eine Callback zur Verfügung. Diese Callback wird immer dann aufgerufen wenn Delphi einem Klassennamens-String NICHT die Klasse zuordnen kann. D.h. wenn alle Stricke reisen und selbst mit Registerclass() kein Erfolg beschieden ist. Nun, ich würde diese Callback überschreiben (sie ist zur Laufzeit immer NIL) und darin mit dem obigen Trick nach einer Klassen-RTTI im Codesegment aller geladenen Module nach dem Klassennamen suchen. Somit überfrachtet man nicht die interne Liste der RegisterClass() Funktion.

Aber im Grunde wäre es für Borland ein leichtes gewesen von vornherein die RTTI's aller Typdeklarationen untereinander als verlinkte Liste im Codesegment zu speichern. Dann wären solche "um die Ecke Tricks" wie mit RegisterClass() etc. pp. garnicht mehr nötig. Der Compiler würde also alle Typdeklarationen, sprich deren RTTI's, untereinander verlinken und pro Modul gäbe es nur einen globalen Zeiger auf den ersten RTTI Record.
Nun, mein obiger Trick geht genau so vor und nutzt einige Eigenheiten wie Code-Aligment dieser RTTI Records aus. Im Grunde also so als wenn diese RTTI Records als verlinkte Liste durch den Compiler gespeichert wurde (was aber nichtreal der Fall ist). Der Trick ist also deterministisch.

Gruß Hagen
  Mit Zitat antworten Zitat