|
![]() |
|
Registriert seit: 6. Mär 2005 Ort: Hannover 16 Beiträge |
#1
Hallo Sir Thornberry!
![]() könntest du eventuell noch schreiben wozu damals Stream-Klassen registriert wurden?
Delphi-Quellcode:
Nicht die Stremklassen wurden registriert, sondern diejenigen Objekte, die man im Stream speichern wollte. Mein TStreamableObject sei ein solches Objekt, das ich auf einem Stream speichern will. Streamable (streamfähig, weiß, wie es sich in den Stream speichert und wieder aus ihm liest) habe ich hier als Name gewählt, um zu unterstreichen, das das Objekt eben fähig (engl. able) sein soll sich in einem Stream zu speichern und auch wieder gelesen zu werden.
//Der Registrierungsrecord war wie folgt definiert
type PStreamRec = ^TStreamRec; TStreamRec = Record //für jedes Objekt eindeutige Nummer //(war fehlerträchtig->(doppelte Nummern)) ObjType: word; //Adresse der VMT VmtLink: word; //Adresse von Load -> //für das Lesen der Daten vom Stream Load: Pointer; //Adresse von Store -> //Screiben in Stream Store: Pointer; //Nächster Registrierungsrecord Next: PStreamRec; end; //Es wurde einfach eine Liste mit den Registrierungsdaten aufgebaut. //Belegt wurden die Felder so, wie ich es im ersten Beitrag gezeigt habe. Man mußte dann für eigene Objekte eigene Load und Store Methoden schreiben:
Delphi-Quellcode:
Damit das Objekt weiiß, wie Datenfelder, untergeordnete Objekte (wie zB. Buttons auf Form) gespeichert werden müssen, damit sie beim wiedereinlesen nicht nur auf irgendwelche Speicherplätze gebracht werden, sondern eindeutig auch dem richtigen Objekt mit der richteigen VMT zugeordnet werden können, war (ist garantiert auch in Delphi) diese Registrierung notwendig. Und wenn sie notwendig ist und unter Delphi aber nicht vom Programmierer vorgenommen wird, muß diese Registrierung irgendwo in den Tiefen der VCL versteckt sein. In TP/BP mußte ich für jedes Objekt einen StreamRec schreiben, der belegt war, wie im ersten Beitrag gezeigt. Die Zahl ist eine laufende Nummer, der VmtLink ist die Adresse der Vmt des zu speichernden Objektes, dann folgen die Adressen von Load und Store, den Methoden, die für das Lesen bzw. Schreiben zuständig sind. Zusätzlich mußte das Objekt mit der Prozedur:
type
TStreamableObject = object(TObject) Datenfeld_1: Integer; Datenfeld_2: PString; //Zeiger auf einen String Datenfeld_3: PObject; //nur beispielhaft (In der Praxis irgendein sinnvoller Objektzeiger) constructor Init; constructor Load(var S: TStream); procedure Store(var S: TStream); virtual; end; //TObject war ganz einfach gestrickt, hatte keine Datenfelder und nur eine virtuelle Methode. //Damit war garaniert das die VMT das erste Feld (Offset 0) eines jeden Objektes war, da alle //Detenfelder erst DANACH folgten. type TObject = Object constructor Init; //Wie in Delphi auch-> sobald virtuelle Methoden-> Konstruktor procedure Free; //nicht virtuell, für Vertändnis hier unwichtig destructor Done; virtual; //Damit gibt es eine VMT end; //Jedes Streamfähige Objekt mußte natürlich von TObject abgeleitet werden, damit das //Konzept funktionierte. constructor TStreamableObject.Init; begin inherited Init; //die einenen Voreinstellungen end; constructor TStreamableObject.Load(var S: TStream); begin //wenn direkt von TObject abgeleitet -> inherited Init; //wenn zwischen meinem Objekt und TObject weitere Klassen dazwischen, dann //inherited Load(S); //um die geerbten Datenfelder auch zu berücksichtigen //Vorausgesetzt, es gibt einen geerbten Load Constructor, ja, Load war ein //Constructor //Hier also, weil direkt von TObject abgeleitet: inherited Init; S.Read(Datenfeld_1); S.ReadStr(Datenfeld_2); //Kommentar-> siehe Kommentare in der Store-Methode GetSubObjPtr(S, Datenfeld_3); // end; procedure TStreamableObject.Store(var S: TStream); begin //Wenn direkt von TObject abgeleitet, wird keine Store Methode geerbt, weil in TObject //keine definiert war, sonst inherited Store(S); S.Write(Datenfeld_1, sizeof(Datenfeld_1); S.WriteStr(Datenfeld_2); //Spezielle Methode, die weiß, wie Zeiger sinnvoll und korrekt //im Stream abgelegt werden PutSubObjPtr(S, Datenfeld_3); //Spezielle Methode, die weiß, wie Objektzeiger im Stream //gespeichert werden end; //Die Reihenfolge der Datenfelder muß beim Lesen und schreiben identisch sein //Nun weiß das Objekt, wie die Daten im Stream gespeichert werden. //Für das folgende Beispiel setze ich voraus, das ich ein Objekt TMyApplication //definiert habe, in welchem mein Objekt vom Stream gelesen und zum Programmende //auch wieder in den Stream geschrieben wird. //In der Anwendung stand dann sowas hier: constructor TMyApplication.Init; begin RegisterStrmObj; //Hier wird mein Objekt registriert inherited Init; //...eigene Voreinstellungen, wie in Delphi auch... end; procedure TMyApplication.LoadThisObjectFromStream; var ObjfromStream: TStreamableObject; Stream: TStream; StreamName: string; begin StreamName := 'MyStream.stm'; Stream.Init(StreamName,stOpenRead); ObjFromStream := PStreamableObject(Stream.Get); //Die Typumwandlung war nötig, weil der Stream Objekte oder Nachfahren vom Typ TObject //speicherte //Natürlich muß ich vorher den Typ PStreamableObject definiert haben als ^TStreamableObject; Stream.Done; //in Delphi Destroy end; //Analog konnte das Objekt gespeichert werden: procedure TMyApplication.SaveThisObjectToStream; var Stream: TStream; StreamName: string; begin StreamName := 'MyStream.stm'; Stream.Init(StreamName,stOpenRead); Stream.Put(MyObjectOnStream); //Die Typumwandlung war nötig, weil der Stream Objekte oder Nachfahren vom Typ TObject //speicherte //Natürlich muß ich vorher den Typ PStreamableObject definiert haben als ^TStreamableObject; Stream.Done; //in Delphi Destroy end; RegisterType(RMyObjectWhichShouldWorkWithStream); registriert werden. Deshalb die TStreamRec Konstante RStreamableObject, per Konvention, wie der Objekttyp, aber statt Präfix T wurde Präfix R davor gesetzt. Das wurde (dann auch per Konvention) so gemacht, das man eine Prozedur (KEINE METHODE) geschrieben hat: procedure Register[gefolgt vom unitnamen]; ---> siehe erster Beitrag Meine Beispielunit heißt StrmObj -> also
Delphi-Quellcode:
In der Anwendung wurde diese dann vom InitKonstruktor augerufen, womit das Objekt für die Zusammenarbeit mit dem Stream registriert, also vorbereitet war.
procedure RegisterStrmObj;
begin RegisterType(RStreamableObject); end; Ich bin sicher, das in Delphi die Klassen genauso für die Arbeit mit Streams registriert werden aber die Registrirung geschickt vor dem Programmierer verborgen wird und automatisch erledigt wird. Als erstes fallen mir da die Konstruktoren ein. Aber dann müßte ich ja bei Einführung neuer Datenfelder auch wieder selber Hand anlegen. Es muß also einen anderen Trick geben. Die Vorgehensweise in TP/BP war nämlich recht Fehlerträchtig, da eine Registrierung schnell vergessen wird. Delphi nimmt da ne ganze Menge Arbeit ab, aber was geht da in der VCL vor? Also, nicht die Streams, sondern die Objekte, die mit Streams zusammenarbeiten sollten, wurden registriert, dem Stream bekannt gemacht. Grüße von Traudix
Suche die Herausforderung!
|
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |