[Delphi] Subcomponent streamen die een protected property is

Pagina: 1
Acties:

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
Niet-published properties van een component kun je in Delphi streamen door DefineProperties te overriden. Dat lukt allemaal prima voor eenvoudige properties zoals strings en integers. Maar nu wil ik een (protected) property streamen die een TComponent descendant is. Wat een gekloot :(.

De property heet XMLDocument en is een TXMLDocument.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
procedure TMijnComponent.DefineProperties(Filer: TFiler);
  function XMLDocumentStored: Boolean;
  begin
    if Assigned(Filer.Ancestor) then
      Result := XMLDocument <> TMijnComponent(Filer.Ancestor).XMLDocument
    else
      Result := Assigned(XMLDocument);
  end;
begin
  inherited DefineProperties(Filer);
  Filer.DefineProperty('XMLDoc', ReadXMLDocument, WriteXMLDocument,
    XMLDocumentStored);
end;

procedure TMijnComponent.ReadXMLDocument(Reader: TReader);
begin
  // Reader.ReadBoolean;
  XMLDocument := TXMLDocument(Reader.ReadComponent(nil));
end;

procedure TMijnComponent.WriteXMLDocument(Writer: TWriter);
begin
  // Writer.WriteBoolean(True);
  Writer.WriteComponent(XMLDocument);
end;
Nu loopt het al mis bij het wegschrijven in WriteXMLDocument - er volgt een 'Out of Memory' foutmelding. Aan het teruglezen kom ik niet eens toe.

Het stomme is dat als ik in WriteXMLDocument een WriteBoolean() toevoeg, het schrijven wel lukt en er geen foutmelding volgt. Het probleem verplaatst zich dan echter naar het inlezen: ReadXMLDocument heeft er absoluut geen zin in.

Bovendien heb ik tijdens veel klooien gezien dat WriteComponent() de eigenschappen van XMLDocument wegschrijft alsof ze eigenschappen waren van TMijnComponent (en dat moet natuurlijk niet). De .dfm file van een TForm met daarop een TMijnComponent ziet er dan zo uit:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
object Form1: TForm1
  Left = 431
  Top = 280
  ...
  object MijnDocument1: TMijnDocument
    AutoSave = True
    Left = 88
    Top = 184
    XMLDoc = True
    DOMVendorDesc = 'MSXML'
  end
end
De property DOMVendorDesc (van TXMLDocument) wordt door WriteComponent dus weggeschreven alsof het een property van TMijnDocument was met daarboven de waarde van XMLDoc. Natuurlijk gaat het dan bij het inlezen helemaal mis: er volgt een stream error.

Er staat trouwens een voorbeeld in de helpfiles van Delphi 7 waarin een component wordt weggestreamd, maar zelfs dat voorbeeld werkt bij mij niet naar behoren :( (zelfde klachten als hierboven aangegeven). Nog even voor de duidelijkheid, het probleem zit in ReadXMLDocument en WriteXMLDocument, niet in DefineProperties.

Ik sta op het punt het op te geven. Heeft iemand een geniale oplossing?

[ Voor 71% gewijzigd door Tomatoman op 03-11-2003 02:13 . Reden: Code maar even opgemaakt als 'C++' zodat de leestekens weer leesbaar zijn. ]

Een goede grap mag vrienden kosten.


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
*kick*

Waar zijn al die Delphi-goeroes?

Een goede grap mag vrienden kosten.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

Ik ben geen Delphi-goeroe maar ken wel VCL, mag ik dan ook reageren? ;)

Zoals je het beschrijft klinkt het mij als een botte bug in de oren, en helaas ben ik dat van Borland wel gewend. Ik zou als ik jou was eens de exacte VCL sources erbij pakken die de corrupte resultaten produceren, en die minutieus stap-voor-stap analyseren en doorrekenen aan de hand van je ingevoerde parameters. Je kunt de VCL code ook inline in je code pasten natuurlijk, handig voor debuggen.

Ik heb vaker ernstige bugs in Borland-produkten gevonden, dus kijk niet te vreemd op als je iets stupides vind... heb helaas zelf maar BCB 5 dus kan je verder niet echt helpen.

Professionele website nodig?


  • cimbom
  • Registratie: Juni 2001
  • Laatst online: 26-04-2024

cimbom

Just Kidding

zo uit delphi 7 help, mischien moet je die read en write zo aanpassen?
Ik weet ook niet wanneer je streamen zou gebruiken, maar dit is enige verschil die ik zie vanuit delphi help.
Delphi:
1
2
if Reader.ReadBoolean then
    XMLDocument := TXMLDocument(Reader.ReadComponent(nil));


Delphi:
1
2
3
Writer.WriteBoolean(XMLDocument <> nil);
  if XMLDocument <> nil then
    Writer.WriteComponent(XMLDocument);



edit:
ook hier zie ik vershil ik weet niet of het uitmaakt:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function XMLDocumentStored: Boolean; 
begin
if Assigned(Filer.Ancestor) then
begin
  if TMijnComponent(Filer.Ancestor).XMLDocument = nil then
    Result := XMLDocument <> nil
  else if XMLDocument = nil or
          TMijnComponent(Filer.Ancestor).XMLDocument.Name <> XMLDocument.Name then
    Result := True
  else Result := False;
end
else
  Result := Assigned(XMLDocument);
end;


Dus mijn code voorbeelden zijn jouw code die conform delphi help voorbeeld is.


edit2:

Nou mischien toch dat het alleen aan volgende ligt :
jouw code
Delphi:
1
2
Filer.DefineProperty('XMLDoc', ReadXMLDocument, WriteXMLDocument, 
    XMLDocumentStored);


wat het moet zijn?
Delphi:
1
2
Filer.DefineProperty('XMLDocument', ReadXMLDocument, WriteXMLDocument, 
    XMLDocumentStored);

[ Voor 53% gewijzigd door cimbom op 04-11-2003 15:42 ]


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Ik heb het antwoord ook niet direct, maar ik zou eens kijken naar de volgende functie:
Delphi:
1
procedure TComponent.GetChildren(Proc: TGetChildProc; Root: TComponent);

Probeer ook dit eens:
Delphi:
1
2
3
4
5
6
procedure TMijnComponent.ReadXMLDocument(Reader: TReader); 
begin 
  // Reader.ReadBoolean; 
  XMLDocument := TXMLDocument(Reader.ReadComponent(XMLDocument));  
  // Wel XMLDocument creeren in de constructor!
end;

of
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
procedure TMijnComponent.DefineProperties(Filer: TFiler); 
  function XMLDocumentStored: Boolean; 
  begin 
    if Assigned(Filer.Ancestor) then 
      Result := XMLDocument <> TMijnComponent(Filer.Ancestor).XMLDocument 
    else 
      Result := Assigned(XMLDocument); 
  end; 
begin 
  inherited DefineProperties(Filer); 
  Filer.DefineBinaryProperty('XMLDoc', ReadXMLDocument, WriteXMLDocument, 
    XMLDocumentStored); 
end; 


Als ik meer tijd heb zal ik er even langer naar kijken.

/edit
* LordLarry gaat nog maar eens zijn 'Delphi Component Design - Danny Thorpe' doornemen...

[ Voor 6% gewijzigd door LordLarry op 04-11-2003 20:20 ]

We adore chaos because we like to restore order - M.C. Escher


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
Ook de hulp van C++ Builder VCL-goeroes is welkom :)
cimbom schreef op 04 november 2003 @ 15:27:
zo uit delphi 7 help, mischien moet je die read en write zo aanpassen?
Ik weet ook niet wanneer je streamen zou gebruiken, maar dit is enige verschil die ik zie vanuit delphi help.
Delphi:
1
2
if Reader.ReadBoolean then
    XMLDocument := TXMLDocument(Reader.ReadComponent(nil));
Delphi:
1
2
3
Writer.WriteBoolean(XMLDocument <> nil);
  if XMLDocument <> nil then
    Writer.WriteComponent(XMLDocument);
In essentie is dit dezelfde code als de mijne met de uitgecommentarieerde regels 17 en 23 weer teruggezet naar code. Helaas blijkt dat niet de oplossing bij het lezen. Er wordt ook geen subcomponent weggeschreven, maar alleen wat ik al had weergegeven.

@edit1: Zelfs het voorbeeld uit de helpfiles letterlijk overnemen werkt niet :|

@edit2: Ook de naam van de 'dummy' property veranderen naar bijvoorbeeld 'XMLDocument' heeft geen effect.
LordLarry schreef op 04 november 2003 @ 20:16:
Ik heb het antwoord ook niet direct, maar ik zou eens kijken naar de volgende functie:
Delphi:
1
procedure TComponent.GetChildren(Proc: TGetChildProc; Root: TComponent);
He, da's nieuw voor mij. Ik zie nog niet direct een opening voor een oplossing, maar ik ga er eens mee stoeien.
Probeer ook dit eens:
Delphi:
1
2
3
4
5
6
procedure TMijnComponent.ReadXMLDocument(Reader: TReader); 
begin 
  // Reader.ReadBoolean; 
  XMLDocument := TXMLDocument(Reader.ReadComponent(XMLDocument));  
  // Wel XMLDocument creeren in de constructor!
end;
Al gedaan. Resultaat: geen verschil.
of
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
procedure TMijnComponent.DefineProperties(Filer: TFiler); 
  function XMLDocumentStored: Boolean; 
  begin 
    if Assigned(Filer.Ancestor) then 
      Result := XMLDocument <> TMijnComponent(Filer.Ancestor).XMLDocument 
    else 
      Result := Assigned(XMLDocument); 
  end; 
begin 
  inherited DefineProperties(Filer); 
  Filer.DefineBinaryProperty('XMLDoc', ReadXMLDocument, WriteXMLDocument, 
    XMLDocumentStored); 
end; 
Daar heb je een goed punt. Al heb ik het nog niet geprobeerd, uit eerdere experimenten weet ik dat dit wel gaat lukken. In feite maakt het dan niet meer uit of je een string, component, image of wat dan ook streamt, het wordt allemaal behandeld als binaire rommel. Dat werkt natuurlijk wel, maar eigenlijk haal je daarmee het principe onderuit dat je componenten ook als componenten streamt en niet als binaries. In een .dfm file valt daar niets meer aan te sleutelen, terwijl je dat wel kunt doen met 'echte' gestreamde componenten (die worden immers als plain text opgeslagen).

Het alternatief is dat ik alle published properties van TXMLDocument stuk voor stuk stream met iets als
Delphi:
1
2
3
4
  Filer.DefineProperty('DOMVendor', ReadDOMVendor, WriteDOMVendor, 
    DOMVendorStored); 
  Filer.DefineProperty('XML', ReadXML, WriteXML, XMLStored);
  ...
Dat is niet de fraaiste oplossing maar het werkt wel.


Enne, bedankt allemaal voor het meedenken tot zover :)

[ Voor 6% gewijzigd door Tomatoman op 04-11-2003 23:10 . Reden: Toch maar eens leren lezen voordat ik op 'Verstuur bericht' druk. ]

Een goede grap mag vrienden kosten.


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

tomatoman schreef op 04 november 2003 @ 23:06:He, da's nieuw voor mij. Ik zie nog niet direct een opening voor een oplossing, maar ik ga er eens mee stoeien.
Volgensmij wordt het dit:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
procedure TMijnComponent.GetChildren(Proc: TGetChildProc; Root: TComponent);
begin
  inherited GetChildren(Proc, Root);

  if Assigned(XMLDocument) then
    Proc(XMLDocument);
end;

// Deze moet ook nog misschien:
function TMijnComponent.GetChildOwner: TComponent;
begin
  Result := Self;
end;

En de rest van je DefineProperties code mag dan weg.

We adore chaos because we like to restore order - M.C. Escher


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
LordLarry zij geprezen, GetChildren lijkt de oplossing _/-\o_. Toch maar eens kijken of ik dat boek van Danny Thorpe kan krijgen :). Ik heb het zaakje nog niet helemaal werkend, maar in ieder geval wordt XMLDocument nu netjes weggeschreven.

Een goede grap mag vrienden kosten.


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

No problemo. Glad to be of service.

Danny Thorpe's boek is een van de weinige Delphi boeken die ingaat op de innerlijke werking van Delphi, zoals bijvoorbeeld Streaming en RTTI. Bovendien wordt er ook in uitgelegt waarom bepaalde keuzes door Borland zijn gemaakt. Dat kan Thorpe schrijven, want hij was nauw betrokken bij de ontwikkeling van Delphi. Hij is het trouwens nog steeds, maar nu als leading architect. Hij heeft o.a. de compiler geschreven voor Delphi 8 for .Net. Het is wel een oud boek (Delphi 2), maar de informatie is grootendeels nog steeds bruikbaar. Het boek wordt al een tijdje niet meer gedrukt en is gewilt, dus de prijs is aardig hoog. Maar voor een Delphi liefhebber en VCL componenten schrijver zeer de moeite waard.

Zie de 'Used' sectie op http://www.amazon.com/exe...v=glance&s=books&n=507846 Op amazon.co.uk durven ze zelfs 151 Britse ponden voor dit boek te vragen!

[ Voor 3% gewijzigd door LordLarry op 06-11-2003 09:43 ]

We adore chaos because we like to restore order - M.C. Escher

Pagina: 1