AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Kniffliges Problem beim Serialisieren

Ein Thema von SMALLID · begonnen am 4. Feb 2007 · letzter Beitrag vom 10. Feb 2007
Antwort Antwort
Seite 1 von 3  1 23   
SMALLID

Registriert seit: 10. Aug 2004
78 Beiträge
 
#1

Kniffliges Problem beim Serialisieren

  Alt 4. Feb 2007, 10:20
Hello party people,

also ich bin da auf ein sehr interessantes, wenn auch frustrierendes, und zugleich möglicherweise sogar noch logisches Problem gestoßen, an dem sich jeder gerne versuchen darf

Hintergrund: Entwickelt wurde eine MDI-Anwendung. In den jeweiligen MDI-Children werden in zahlreichen Controls Eingaben gemacht, die in ihrer Gesamtheit gespeichert und später wieder geladen werden sollen. Damit ich das nicht in der Manier:

"Schreibe KomponenteX.EigenschaftA in TextDatei t, gehe eine Zeile weiter, Schreibe KomponenteY.EigenschaftB in TextDatei t, gehe ..."

machen muss, habe ich das (auch dank einiger Beispiele von DelphiPraxis ) so gelöst:

Speichern:
Delphi-Quellcode:
procedure TMainForm.FileSaveAs1Execute(Sender: TObject);
var
  FileStream : TFilestream;
begin
  if SaveDialog1.Execute then
  begin
    FileStream:=TFileStream.Create(SaveDialog1.Filename, fmCreate);
    Try
      FileStream.WriteComponent(MainForm.ActiveMDIChild);
    Finally
      FileStream.Free;
    end;
  end;
end;
Laden:
Delphi-Quellcode:
procedure TMainForm.ToolButton12Click(Sender: TObject);
var
  Child: TForm;
  FileStream : TFileStream;
begin
  if OpenDialog.Execute then
  begin
    FileStream:=TFileStream.Create(OpenDialog.Filename, fmOpenRead);
    Try
      //Eine Instanz eines Unterfensters wird erzeugt
      Child := TMDIChild.Create(MainForm);
      //Alle Enthaltenen Komponenten darin müssen aber erst gelöscht werden,
      //da beim Laden sonst eine Komponente mit demselben Namen schon existieren
      //würde. Ist quick'n'dirty, aber es funktioniert erstmal.
      While Child.ComponentCount > 0 do
      begin
        Child.Components[0].Destroy;
      end;
      //Der Stream mit allen Eigenschaften und Unterkomponenten wird nun in Child hineingelesen
      FileStream.ReadComponent(Child);
    Finally
      FileStream.Free;
    end;
  end;
end;
So das funktioniert auch soweit schon ganz gut.
Nun aber das Problem:
Aus den gesetzten Eigenschaften werden zur Laufzeit (eigene) Komponenten erzeugt (wen's interessiert: es handelt sich dabei um simple Software-Agenten, die miteinander auf einem simulierten Markt handeln sollen, sowie "Agenten-Pools", dem die Agenten angehören.)
Kurz gesagt werden die dynamisch erzeugten Komponenten weder komplett geschrieben (das ist aber nur ein educated guess, weil ich mir die Datei mal im Wordpad angeschaut hab) noch geladen. Wenn ich das Teil wieder lade, wird nicht mehr "gehandelt".
Nähere Infos:
Es wird zuerst ein Agenten-Pool (auch: "MarketCoordinator") erzeugt. Das ist eine simple Spezialisierung von TComponent mit einigen zusätzlichen Eigenschaften. Der Owner dieser Komponente ist das MDI-Child. Dann werden die Agenten erzeugt. Auch sie sind Spezialisierungen von TComponent mit einigen eigenschaften und der methode "handeln()". Der Owner eines jeden Agenten ist der angegebene Agenten-Pool.
Der Name und der Typ des Agent-Pools stehen in der gespeicherten Datei an letzter Stelle (siehe Anhang). Mehr aber nicht. Die Agenten tauchen so wie ich das sehe gar nicht auf.

Meine Vermutung:
Ohne euch auf eine falsche Spur zu kriegen! Kann es evtl. daran liegen, dass der Typ TMDIChild zwar mit den einzelnen Controls definiert ist (die stehen ja auch zur Entwurfszeit alle drin in der Definition/Formulardatei), aber nicht mit den dynamisch erzeugten Komponenten? Kann mir das nicht so richtig vorstellen, denn der Agenten-Pool wird ja zumindest mit seinem Namen auch mitgespeichert und der wurde ja dynamisch erzeugt.

Habe auch schon versucht, mit der procedure "setSubComponent" was zu reißen, aber das scheint nichts zu bringen - ist wohl für die Entwurfszeit gedacht
Angehängte Dateien
Dateityp: txt versuchd_756.txt (23,9 KB, 16x aufgerufen)
Muh
  Mit Zitat antworten Zitat
SMALLID

Registriert seit: 10. Aug 2004
78 Beiträge
 
#2

Re: Kniffliges Problem beim Serialisieren

  Alt 5. Feb 2007, 15:52
Update: Nur zur Info: Meine Vermutung ist vermutlich falsch . Es liegt an etwas anderem. Wenn ich nur eine Unterkomponente speichere (z.B. sage ich myStream.WriteComponent(AgentPool1)) werden deren Eigenschaften (eigentlich nur name und tag und sowas gespeichert, aber nicht die in "AgentPool1.Components[]" enthaltenen Komponenten. Die Kinder werden also einfach nicht geschrieben. Bleibt die Frage, warum?

Wenn jemand sich der Sache mal annehmen will aber mehr infos braucht, besorg ich die euch gerne. Naja, es ist nun nicht so, als ob das ein Riesenproblem ist. Ist ja eine grundlegende Sache, wenn das nicht richtig gestreamt wird. Aber halt eine schwerwiegende
Muh
  Mit Zitat antworten Zitat
marabu

Registriert seit: 6. Apr 2005
10.109 Beiträge
 
#3

Re: Kniffliges Problem beim Serialisieren

  Alt 6. Feb 2007, 09:09
Hallo,

Zitat von SMALLID:
Ist ja eine grundlegende Sache, wenn das nicht richtig gestreamt wird. Aber halt eine schwerwiegende
ich glaube schon, dass "richtig gestreamt" wird - vielleicht hast du eine falsche Erwartungshaltung? Deine Agents und AgentPools verstellen nur den Blick auf das Wesentliche. Erstelle mal ein Mini-Projekt, welches dein Streaming-Problem aufweist, hänge es hier rein - dann werden wir das Rätsel schon lösen. Automatisches streaming findet laut Dokumentation nur mit published properties statt - wenn ich mich recht erinnere.

Grüße vom marabu
  Mit Zitat antworten Zitat
SMALLID

Registriert seit: 10. Aug 2004
78 Beiträge
 
#4

Re: Kniffliges Problem beim Serialisieren

  Alt 6. Feb 2007, 19:00
Ich habe mal ein einfachst gehaltenes Projekt mit wenigen Zeilen erstellt, welches das Problem verdeutlicht:

(siehe Anhang)
Dabei wird über den ersten Button eine Komponente created, deren Owner das Fenster ist. Der zweite Button erzeugt dann eine KOmponente, deren Owner die erste Komponente ist. Der dritte Button speichert das Ganze. Wenn ihr euch dann mal die gespeicherten Dateien anseht (am besten die Textform), seht ihr, dass am Ende die erste Komponente ("ComponentA") mit gespeichert wurde. Die zweite wird aber dann nicht mitgespeichert.
Meine Frage ist nun, warum das so ist? Ich versuche schon massig Workarounds zu basteln. Bisher mit mäßigem Erfolg.

Code:
object Form1: TForm1
  Left = 46
  Top = 46
  ActiveControl = Button3
  Caption = 'Form1'
  ClientHeight = 204
  ClientWidth = 470
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  Position = poDesigned
  Visible = True
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 32
    Top = 8
    Width = 154
    Height = 25
    Caption = 'ComponentA.Create(Form1)'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 32
    Top = 56
    Width = 209
    Height = 25
    Caption = 'ComponentB.Create(ComponentA)'
    TabOrder = 1
    OnClick = Button2Click
  end
  object Button3: TButton
    Left = 32
    Top = 104
    Width = 89
    Height = 25
    Caption = 'Speichern'
    TabOrder = 2
    OnClick = Button3Click
  end
  object ComponentA: TComponent
  end
end
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button3Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  ComponentA : TComponent;
begin
  ComponentA := TComponent.Create(self);
  ComponentA.Name := 'ComponentA';
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  ComponentB : TComponent;
begin
  ComponentB := TComponent.Create(FindComponent('ComponentA'));
  ComponentB.Name := 'ComponentB';
// ComponentB.setSubComponent(true);
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  FileStream : TFilestream;
  StringStream : TStringStream;
  StringList : TStringList;
begin
    FileStream:=TFileStream.Create('speichern-binaer.dat',fmCreate);
    StringStream := TStringStream.Create('');
    Try
      FileStream.WriteComponent(Form1);
      FileStream.Position := 0;
      ObjectBinaryToText(FileStream, StringStream);
      StringList := TStringList.Create;
      StringList.add(StringStream.DataString);
      StringList.SaveToFile('speichern-textform.txt');
    Finally
      FileStream.Free;
      StringStream.Free;
    end;
end;
end.
Angehängte Dateien
Dateityp: zip speichern-problem_646.zip (236,6 KB, 5x aufgerufen)
Muh
  Mit Zitat antworten Zitat
oki

Registriert seit: 30. Dez 2002
Ort: Brandshagen
1.819 Beiträge
 
Delphi 2007 Professional
 
#5

Re: Kniffliges Problem beim Serialisieren

  Alt 6. Feb 2007, 20:23
Hi,

hast du es schon mal mit Kompos zur Designzeit probiert?

In der Regel reicht beim Create von Compos zur Laufzeit der Owner im Create nicht aus. Es muß explizit der Parent gesetzt werden.

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  ComponentB : TComponent;
begin
  ComponentB := TComponent.Create(FindComponent('ComponentA'));
  ComponentB.Parent := ComponentA;
  ComponentB.Name := 'ComponentB';
// ComponentB.setSubComponent(true);
end;
Dabei ist wir aber noch nicht ganz klar warum ComponentA der Owner sein soll. Ich gehe mal davon aus, dass alle compos auf Form1 plaziert sein sollen. Dann würde ich immer Form1 als Owner und Parent halten. Für deine Hirarchie halte ich es für besser einen eigenen Member (Property) anzulegen.

Das mit dem Parent ist aber nur eine Vermutung.

Die OH sagt auch folgendes
Zitat:
WriteComponent erstellt ein Writer-Objekt und ruft dessen Methode WriteRootComponent auf, um die in Instance angegebene Komponente und deren untergeordnete Objekte in den Stream zu schreiben.
Da steht nichts von Rekursion! Die einzige Untercompo von Form1 ist ComponenteA. ComponenteB ehört nicht mehr dazu. Wenn du das gleiche mit ComponentA machst, wird sicher ComponentB zu sehen sein, aber keine weiteren Unterkomponenten.

P.S. Ich denke das mit dem Parent war nicht.

Gruß oki
  Mit Zitat antworten Zitat
SMALLID

Registriert seit: 10. Aug 2004
78 Beiträge
 
#6

Re: Kniffliges Problem beim Serialisieren

  Alt 6. Feb 2007, 20:52
Infos:
- Die ComponentA soll deshalb ein Besitzer von ComponentB sein, weil sich sozusagen um eine Komposition bzw. Aggregation handelt. Die Agenten sollen einem AgentenPool angehören. Beim starten der Tätigkeiten des jeweiligen Agentenpools braucht der dann nur noch seine Kinder durchzugehen (for COmponents[i]) und die jeweils was machen zu lassen. Das wäre umständlicher, wenn die Agenten alle dem Formular gehören würden und jeder Pool sich erst wieder seine Agenten suchen müsste.

- Die Eigenschaft Parent gibt es meines Wissens nach erst ab TControl

- SetSubComponent sollte das mit der Rekursion eigentlich erledigen, hatte ich gehofft. Dem ist aber nicht so. Das ist das was mich stutzig macht. Beim Speichern eines Formulars in Delphi geht sowas doch normalerweise auch.

- Was mich noch stutziger macht, ist, dass eben der Aufruf "WriteComponent(FindComponent('ComponentA'))" statt "WriteComponent(Form1)" beim Speichern nicht darin resultiert das nun ComponentA und dann ComponentB als deren Kind geschrieben wird, sondern ebenfalls nur ComponentA in der Datei steht.

Danke für eure Hilfe! Würde mich freuen, wenn wir das Problem lösen können.
Muh
  Mit Zitat antworten Zitat
oki

Registriert seit: 30. Dez 2002
Ort: Brandshagen
1.819 Beiträge
 
Delphi 2007 Professional
 
#7

Re: Kniffliges Problem beim Serialisieren

  Alt 6. Feb 2007, 21:07
Zitat von SMALLID:
Infos:
- Die Eigenschaft Parent gibt es meines Wissens nach erst ab TControl
Jooop! Da hab ich nicht aufgepasst. Das mit den Ownern war mir im Nachgang schon klar. Macht auch Sinn.
Dann bleibt dir wohl nur einen eigenen Algotythmus zu schreiben.

Gruß oki
  Mit Zitat antworten Zitat
SMALLID

Registriert seit: 10. Aug 2004
78 Beiträge
 
#8

Re: Kniffliges Problem beim Serialisieren

  Alt 6. Feb 2007, 21:33
Naja können wir das jetzt als Bug bezeichnen und melden es CodeGear? Ich meine, probiert das mit dem Speichern von ComponentA statt Form1 ruhig mal aus. Da wird keine Unterkomponente gespeichert, auch wenn das so in der Onlinehilfe steht. Bei Form1 werden sie aber gespeichert (dann eben nur 1 Level). Ein bissl inkonsistent ... oder ich hab' wirklich was übersehen.

Ich werd das jetzt Workaroundmäßig mal versuchen, so zu lösen:
Ich rufe WriteComponent(FormX) auf. Dann schaue ich hartcodiert nach ob in FormX Komponenten vom Typ "ComponentA" enthalten sind (Also genauer: AgentPools). Dann mache ich für jeden AgentPool eine neue Datei auf und gebe ihr den Namen "[Name des Agentpools].dat". Da rein werden dann die Kinder des AgentPools, also die Agenten bzw. im vereinfachten Bsp. alle ComponentB's geschrieben.
Beim Lesen läuft es dann analog. Erst wird die Form geladen. Dann wird nach ComponentA's bzw. "AgentPools" geschaut, deren Name+'.dat' dann wieder den Zugriff auf den Stream mit den ComponentB's, sprich: Agents, ermöglicht.
Meiner Meinung nach eine sehr unelegante und unschöne Lösung, aber ein Workaround muss ja offenbar her.
Muh
  Mit Zitat antworten Zitat
marabu

Registriert seit: 6. Apr 2005
10.109 Beiträge
 
#9

Re: Kniffliges Problem beim Serialisieren

  Alt 7. Feb 2007, 16:47
Hallo,

Zitat von SMALLID:
Ein bissl inkonsistent ... oder ich hab' wirklich was übersehen.
dass nur ComponentA gespeichert wird, entspricht völlig den Regeln. Damit Sub-Komponenten gespeichert werden, musst du laut Dokumentation die Methode GetChildren() überschreiben, welche bei der Basisklasse TComponent einfach gar nichts macht.

Grüße vom marabu
  Mit Zitat antworten Zitat
SMALLID

Registriert seit: 10. Aug 2004
78 Beiträge
 
#10

Re: Kniffliges Problem beim Serialisieren

  Alt 7. Feb 2007, 18:52
Dann hab' ich das tatsächlich übersehen und mir die Workaround-Arbeit wahrscheinlich umsonst gemacht. Ich würde das mit dem override von GetChildren aber dennoch noch einmal probieren. Kannst du mir dazu nochmal das Hilfethema sagen, wo das stand?
Muh
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 3  1 23   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:08 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz