Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi TStream in (dp)CollectionItem speichern (https://www.delphipraxis.net/74824-tstream-dp-collectionitem-speichern.html)

jensw_2000 9. Aug 2006 10:21


TStream in (dp)CollectionItem speichern
 
Moin,

ich komme bei meiner ersten Verwendung der dpCollection nicht weiter.

Prinzipiell soll die Collection mit ein einziges Item besitzen.
Dieses Item beinhaltet ein paar einfache String und Integer Properties sowie eine TStream Instanz.

Grundlegend soll das Ganze ein ZLib Archiv mit ein paar zusätzlichen Header-Informationen werden, die später sicher erweitert werden.

Die ItemClass ist wie folgt definiert:

Delphi-Quellcode:
unit uArchiveCollectionItem;

interface

uses Windows, classes, JvComponent,JvZlibMultiple,dpCollection;

type
  TArchiveItem = class(TCollectionItem)
  private
    fItemName:String;
    fAudioFileCount:Integer;
    fAudioArchive:TStream;

    procedure AssignAudioArchive(Source:TStream);

  public
    procedure LoadAudioData(AudioFiles:TStringList);
 
  published
    Property ItemName:String
           Read fItemName
       Write fItemName;
    Property AudioFileCount:integer
       Read fAudioFileCount
       Write fAudioFileCount;
  protected
end;

implementation

//////////////////////////////////////////////////////////////
// Implementation TArchiveItem
//////////////////////////////////////////////////////////////

procedure TArchiveItem.LoadAudioData(AudioFiles:TStringList);
   var AudioFileCompressor:TJvZlibMultiple; // Jedi ZLib Archiver
begin

  fAudioFileCount := AudioFiles.Count;

  if AudioFiles.Count > 0 then
  begin

    AudioFileCompressor:=TJvZlibMultiple.Create(nil);
    try
      AudioFileCompressor.StorePaths:=false;
      AssignAudioArchive(AudioFileCompressor.CompressFiles(AudioFiles));  
      // CompressFiles liefert TStream als Result
    finally
      AudioFileCompressor.free;
    end;

  end
  else begin

    if assigned(fAudioArchive) then fAudioArchive.free

  end;
end;

procedure TArchiveItem.AssignAudioArchive(Source:TStream);
begin
  if not assigned(fAudioArchive) then fAudioArchive:=TStream.Create;
  //  fAudioArchive.CopyFrom(Source,Source.size); >> liefert Exception "Abstrakter Fehler"
  //  fAudioArchive:=Source; >> TmxJSCollection wird mit einem leeren CollectionItem gespeichert
  fAudioArchive:=Source;
end;

end.
Ursprünlich hatte die ItemClass noch einen Construktor und einen Destruktor, in dem ich den die TStream Instanz erstellen / freigeben wollte.

Der Konstruktor wurde bei " MyItem:= MyCollection.add as TArchiveItem " nicht aufgerufen.
Wenn ich ihn überschrieben habe (constructor create(Collection:TCollection); override; ) dann bekam ich einen Abstrakten Fehler...

Um das Problem zu umgeben, erstelle ich die Stream Instanz (fAudioArchive) vorerst zu einem anderen Zeitpunkt.


Mein Hauptproblem liegt hier:

Delphi-Quellcode:
procedure TArchiveItem.AssignAudioArchive(Source:TStream);
begin
  if not assigned(fAudioArchive) then fAudioArchive:=TStream.Create;
  //  Variante 1
  // fAudioArchive.CopyFrom(Source,Source.size);
  // >> liefert hier eine Exception "Abstrakter Fehler"

  //  Variante 2
  // fAudioArchive:=Source;
  // >> TmxJSCollection wird mit einem leeren CollectionItem gespeichert
  fAudioArchive:=Source;
end;

Die ItemClass wird wie folgt verwendet:

Delphi-Quellcode:
Procedure Tmain.Save(Sender TObject);
  var Coll: TmxJsCollection;
      ArchiveItem. TArchiveItem;
begin

  Coll := TmsJsCollection.Create(TArchiveItem);
  try
    ArchiveItem := Coll.Add as TArchiveItem;

    ArchiveItem.Call_EndTime:=now;
    ArchiveItem.ItemName := 'einItem';
    ArchiveItem.LoadAudioData(AudioFileList);
    Archive.SaveToFile('c:\test.archive');
  finally
    coll.Free;
  end;
 
end;
Wenn ich den Stream mit Variante 2 zuweise, dann hat mein Testarchiv wird folgenden Inhalt.
Code:
object AudioArchiveCollection: TmxJsCollection
  items = <
    item
    end>
end

Ohne Hilfe komme ich nicht weiter.


Schöne Grüße,
Jens
:hi:

jensw_2000 10. Aug 2006 21:06

Re: TStream in (dp)CollectionItem speichern
 
*push*

Dürfte ich den Thread noch einmal nach oben stellen ?

Ich habe immernoch keine Lösung gefunden.

Versuche ich da etwas, das die DPCollection nicht kann oder sitzt das Problem vor dem Monitor :roll: ?

jensw_2000 16. Aug 2006 11:36

Re: TStream in (dp)CollectionItem speichern
 
*push*

... keiner da, der sich mit der dpCollection auskennt ? :cry:

Eventuell habe ich mich auch unglücklich ausgedrückt.

In einem meiner Projekte werden sensibele Audiodateien erzeugt (Gesprächsmitschnitte).
Diese Daten sollen für einen gewissen Zeitraum verschlüsselt archiviert werden.

Jedes Archiv-File muss einen Header mit ein paar grundlegenden Informationen besitzen (Datum, Zeit, Rufnummern usw.).
Nach dem Header folgt dann ein Stream mit den verschlüsselten Audiodaten.

Die dpCollection bietet mir, wenn ich das richtig herausgelesen habe, den Vorteil, dass ich die Informationen in File-Header erweitern kann, ohne dabei die Kompatibilität mit "alten" Archiven zu verlieren.

Ich komme bei meinen ersten Experimenten mit der DP-Connection nicht weiter.
Dabei möchte ich "einfach" nur eine Collection mit einem einzigen Collection-Item via SaveToStream speichern und später wieder öffnen. Dieses Collection-Item enthält ein paar String- und Integer Werte und einen Stream für die "verschlüsselten" Daten.

Ich wäre überglücklich, wenn sich jemand finden würde, der mit beim Start etwas unter die Arme greift.

Mein Kunde strebt einen Installationstermin in 9 Tagen an :pale:



Schöne Grüße,
Jens
:hi:

jfheins 16. Aug 2006 12:13

Re: TStream in (dp)CollectionItem speichern
 
Sorry, dass ich erst jetzt antworte, aber ich hab' den Thread irgendwie nicht beachtet ^^

Was mir auffällt:
Delphi-Quellcode:
TStream.Create;
TStream ist eine abstrakte Klasse. Und was macht man damit nicht? Genau - verwenden :stupid:
Im Ernst: Verwende dort einen TMemoryStream oder so - dann geht auch das Kopieren ;)

Und der Stream muss natürlich published sein - sonst geht das alles nicht mit der Collection ;)

Soweit so gut ... aber funktioniert es auch?

jensw_2000 16. Aug 2006 16:16

Problem >> Stream in dpCollectionItem speichern
 
Danke, das sieht schon viel besser aus.

Wenn ich die Collection derzeit mit SaveToStream speichere, erhalte ich eine Datei mit folgendem Inhalt:

Code:
object AudioArchiveCollection: TmxJsCollection
  items = <
    item
      Call_Identifier = '2006-08-16-17-04-16-20777'
      ItemName = 'AudioArchiveCollectionItem'
      Call_CallingNumber = '<eine Rummer>'
      Call_internalCalledNumber = '<eine Rummer>'
      Call_StartTime = 38945.711303414360000000
      Call_EndTime = 38945.711511932880000000
      Call_RecordedTime = 13
    end>
end

-- (vorher) ----------------------------------------------
object AudioArchiveCollection: TmxJsCollection
  items = <
    item
    end>
end
----------------------------------------------------------
Das ist schon mal ein Riesen Schritt nach vorn.

Zwei published Properties fehhen aber:
- AudioArchive
- AudioArchiveSize

Warum werden die nicht mitgespeichert ?

[edit]
... Code entfernt.
s. Beispielprojekt in Post #10
// Jens
[/edit}

mschaefer 16. Aug 2006 19:34

Re: TStream in (dp)CollectionItem speichern
 
Hallo Jens,

bin da kein Profi, aber die AudioArchiveSize ist erklärbar: Diese ist nur als ReadOnly definiert. Das heisst, dass Du die ehedem im Quellcode irgendwo vorbelegen mußt. Da ist es einfach intelligent die Property nicht abzuspeichern.

Tja zu dem MemoryStream, dass würde mich auch interessieren! Kann die DPCollection eigentlich auch in XML-Speichern?

Grüße // Martin

jensw_2000 16. Aug 2006 20:44

Re: TStream in (dp)CollectionItem speichern
 
Ich vermute inzwischen, dass das Problem nur bei Variablen auftritt, die durch das CollectionItem selbst gefüllt werden. Alle Properties die von "extern" gesetzt werden, schreibt Collection.SaveToStream brav in die Datei.



Hier die komplette Implementation des CollectionItems...

[edit]
... Code entfernt.
s. Beispielprojekt in Post #10
// Jens
[/edit}

jensw_2000 16. Aug 2006 21:09

Re: TStream in (dp)CollectionItem speichern
 
Zitat:

Zitat von mschaefer
AudioArchiveSize ist erklärbar: Diese ist nur als ReadOnly definiert. Das heisst, dass Du die ehedem im Quellcode irgendwo vorbelegen mußt. Da ist es einfach intelligent die Property nicht abzuspeichern.

Hi Martin, du hast Recht. Wenn ich AudioArchiveSize RW setze wird die Property auch gespeichert.

Zitat:

Zitat von JensW_2000
Ich vermute inzwischen, dass das Problem nur bei Variablen auftritt, die durch das CollectionItem selbst gefüllt werden. Alle Properties die von "extern" gesetzt werden, schreibt Collection.SaveToStream brav in die Datei.

Das ist damit verworfen...
Die Collection speichert nur den MemoryStream nicht ... :?

PS
Die unit uArchiveCollection habe ich auch gleich noch etwas aufgeräumt.

mschaefer 16. Aug 2006 21:25

Re: TStream in (dp)CollectionItem speichern
 
Ist heute Abend zu spät für mich,

aber es wäre möglich nachzuforschen wie eine Bitmap abgespeichert wird.
Vielleicht werden nur bestimmte Streams gespeichet.

Jens, vielleicht solltest Du die Routine als Miniprojekt anhängen, dass verleitet doch eher zum Testen...

Gute Nacht-Grüße // Martin

jensw_2000 16. Aug 2006 22:46

Re: TStream in (dp)CollectionItem speichern
 
Liste der Anhänge anzeigen (Anzahl: 1)
Du hast Recht.
Ein Beispielprojekt erleichtert das Helfen erheblich.

Anbei ein D7 Spielprojekt (Sourcen und Echse).

jfheins 17. Aug 2006 09:17

Re: TStream in (dp)CollectionItem speichern
 
Ich weis jetzt, woran es liegt: Klassen können nur gestreamt werden, wenn sie von TPersistent abstsammen ... TMemoryStream tut das nicht :mrgreen:

Nachdem ich jetzt das Problem kenne, such ich mal nach ner Lösung, ne? ;)

jfheins 17. Aug 2006 10:05

Re: TStream in (dp)CollectionItem speichern
 
Ok ... ich hab' jetzt ne Lösung auch wenn sie zugegeben etwas ... unschön ... ist :stupid:

Und zwar kopierst du deinen Stream einfach in einen Stringstream, und kannst dann die Property Datastring einer normalen String-Property zuweisen - und String-Properties werden ja gespeichert ;)

(Empfehlung von mit: Eine String-Property mit Getter, der Datastring zurückgibt, und einem Setter, der den String befüllt - sodass du nicht dasselbe zweimal inm Speicher hast ;) )

Gut, was ? :mrgreen:

Du kannst auf jeden Fall die Strams nicht so speichern, weil halt nur von TPersistent abgeleitete Klassen abgespeichert werden ...

Khabarakh 17. Aug 2006 10:21

Re: TStream in (dp)CollectionItem speichern
 
Nicht wirklich getestet :mrgreen: .
Delphi-Quellcode:
TSerializableMemoryStream = class(TInterfacedPersistent, IStreamPersist)
  private
    fStream: TMemoryStream;
    procedure LoadFromStream(aStream: TStream);
    procedure SaveToStream(aStream: TStream);
    function GetStream: TMemoryStream;
  protected
    procedure DefineProperties(Filer: TFiler); override;
  public
    destructor Destroy;
    property InnerStream: TMemoryStream read GetStream;
  end;

[...]

{ TSerializableMemoryStream }

procedure TSerializableMemoryStream.DefineProperties(Filer: TFiler);
begin
  Filer.DefineBinaryProperty('InnerStream', LoadFromStream, SaveToStream, true);
end;

destructor TSerializableMemoryStream.Destroy;
begin
  fStream.Free;
end;

function TSerializableMemoryStream.GetStream: TMemoryStream;
begin
  if (fStream = nil) then
    fStream := TMemoryStream.Create();

  Result := fStream;
end;

procedure TSerializableMemoryStream.LoadFromStream(aStream: TStream);
begin
  InnerStream.LoadFromStream(aStream);
end;

procedure TSerializableMemoryStream.SaveToStream(aStream: TStream);
begin
  InnerStream.SaveToStream(aStream);
end;

jfheins 17. Aug 2006 17:08

Re: TStream in (dp)CollectionItem speichern
 
*
Das geht ja wirklich :shock:

:mrgreen:

Also: getestet, das ist das Ergebnis:
Zitat:

object mxJsCollection: TmxJsCollection
items = <
item
ItemName = 'MyCollectionItem-0'
TestString = 'IrgendEinString'
TestInteger = 55
TestDateTime = 38945.971689560190000000
MemoryStream.InnerStream = {
446173206973742065696E20546573742D546578742E0D0A0D 0A446965736572
205465787420736F6C6C746520286E65747465727765697365 2920616C73207B
4D656D6F72797D53747265616D206D697420646572200D0A64 70436F6C6C6563
74696F6E2067657370656963686572742077657264656E2E0D 0A}
end>
end

mschaefer 17. Aug 2006 18:02

Re: TStream in (dp)CollectionItem speichern
 
:cheers: und Grüße // Martin

Khabarakh 17. Aug 2006 18:51

Re: TStream in (dp)CollectionItem speichern
 
Zitat:

Zitat von jfheins
Das geht ja wirklich :shock:

:mrgreen:

Zweifelst du etwa an dem großen Khabarakh Clan Kihm'bar, dem Hüter der Mal'ary'ush?
:stupid:
Ganz ungetestet hätte ich den Code sicher nicht ins Forum gestellt, allerdings bestand der Test eben nur aus einem Byte (ihr dürft gerne raten, mit welchem Inhalt ;) ), binär serialisiert und zudem ohne dpCollection. Und dass es generell möglich sein musste, sieht man ja an TPicture (es sei denn, die Klasse bestünde vollständig aus Compiler-Magic :zwinker: ).

jfheins 17. Aug 2006 19:04

Re: TStream in (dp)CollectionItem speichern
 
Zitat:

Zitat von Khabarakh
Und dass es generell möglich sein musste, sieht man ja an TPicture (es sei denn, die Klasse bestünde vollständig aus Compiler-Magic :zwinker: ).

Das nicht,aber sie ist (im Gegensatz zu unseren Streams) von TPersistent abgeleitet - und das hilft enorm dabei, gespeichert zu werden :stupid:

Aber das mit dem DefineBinaryProperty ist cool ... wenn du das jetzt noch erklären könntest ... :mrgreen:
(Dann könnte man das direkt neben der Collection inne CodeLib stellen ;))

Khabarakh 17. Aug 2006 19:29

Re: TStream in (dp)CollectionItem speichern
 
Zitat:

Zitat von jfheins
Zitat:

Zitat von Khabarakh
Und dass es generell möglich sein musste, sieht man ja an TPicture (es sei denn, die Klasse bestünde vollständig aus Compiler-Magic :zwinker: ).

Das nicht,aber sie ist (im Gegensatz zu unseren Streams) von TPersistent abgeleitet - und das hilft enorm dabei, gespeichert zu werden :stupid:

Klar, mit TStream allein funktioniert es nicht, soweit waren wir doch schon ;) . Und da es kein IPersistent-Interface gibt (-.-), ist die einfachste Lösung eine Wrapper-Klasse, abgeleitet von T(Interfaced)Persistent.

Zitat:

Aber das mit dem DefineBinaryProperty ist cool ... wenn du das jetzt noch erklären könntest ... :mrgreen:
(Dann könnte man das direkt neben der Collection inne CodeLib stellen ;))
Viel mehr als die OH kann ich dazu auch nicht sagen (außer, dass es von TPicture verwendet wird :mrgreen: ). Man erzeugt eine virtuelle published Property und gibt einen Getter und Setter an, woraufhin man so ein schickes Hex-Feld in seiner DFM erhält.

andreash 17. Aug 2006 23:01

Re: TStream in (dp)CollectionItem speichern
 
Guten Abend!

Wieso reicht es nicht aus, TSerializableMemoryStream von TPersistent abzuleiten?

Und muss es nicht
Delphi-Quellcode:
 destructor Destroy; Override;
und im Rumpf
Delphi-Quellcode:
destructor TSerializableMemoryStream.Destroy;
begin
  fStream.Free;
  Inherited;
end;
heißen?

[edit]fStream.Free vor inherited gesetzt. :wink: [/edit]

jensw_2000 18. Aug 2006 00:53

Re: TStream in (dp)CollectionItem speichern
 
Wow, das ist stark und funktioniert wirklich super.

Ein riesen Dankeschön an Euch ... :cheers:



Schöne Grüße,
Jens
:hi:

Khabarakh 18. Aug 2006 14:26

Re: TStream in (dp)CollectionItem speichern
 
Zitat:

Zitat von andreash
Wieso reicht es nicht aus, TSerializableMemoryStream von TPersistent abzuleiten?

Es reicht natürlich aus, aber warum sollte ich das Interface nicht einbinden?

Zitat:

Und muss es nicht
Delphi-Quellcode:
 destructor Destroy; Override;
und im Rumpf
Delphi-Quellcode:
destructor TSerializableMemoryStream.Destroy;
begin
  fStream.Free;
  Inherited;
end;
heißen?
Jupp, ist mir auch aufgefallen (und dann wieder entfallen ;) ). Das kommt davon, wenn man seit fast einem Jahr keine Destruktoren mehr verwendet :mrgreen: .

andreash 19. Aug 2006 11:52

Re: TStream in (dp)CollectionItem speichern
 
Zitat:

Zitat von Khabarakh
Zitat:

Zitat von andreash
Wieso reicht es nicht aus, TSerializableMemoryStream von TPersistent abzuleiten?

Es reicht natürlich aus, aber warum sollte ich das Interface nicht einbinden?

Schade, ich hatte auf ein gutes Argument gehofft, um zu mehr Weisheit zu gelangen. :duck:

mschaefer 19. Aug 2006 15:11

Re: TStream in (dp)CollectionItem speichern
 
Hm: Weisheit ist relativ (weise)!

Es spart dem Compiler einige Bytes, wenn das Interface direkt eingebunden ist.
Schöner zu lesen ist aber die direkte Ableitung, da sie bei vielen Objekten vergleichbar angewendet wird.

Mich würde immernoch interessieren, ob man die DP-Collection irgendwie in eine XML-Datei bekommt..

Grüße // Martin

jensw_2000 19. Aug 2006 20:36

Re: TStream in (dp)CollectionItem speichern
 
Zitat:

Zitat von mschaefer
Mich würde immernoch interessieren, ob man die DP-Collection irgendwie in eine XML-Datei bekommt..

In TCollection ist kein XML implementiert und in der Child-Class TmxJsCollection (dpCollection) wurde ich kein XML rangestrickt.

Bedeutet also Handarbeit.
Vermutlich wird es aber nicht sehr schwehr einen Parser für nicht-binär abgespeicherte Collections zu schreiben, weil sich die Formate sehr ähneln.

Khabarakh 19. Aug 2006 21:26

Re: TStream in (dp)CollectionItem speichern
 
Zitat:

Zitat von mschaefer
Es spart dem Compiler einige Bytes, wenn das Interface direkt eingebunden ist.
Schöner zu lesen ist aber die direkte Ableitung, da sie bei vielen Objekten vergleichbar angewendet wird.

Öhm... beim Arbeiten mit Interfaces ist das Verkleinern der PE mein letzter Gedanke. Besser gesagt habe ich daran noch nie gedacht. Aber aus modelltechnischer Sicht wäre es doch absoluter Wahnsinn, mehrere Klassen mit den gleichen Methoden (z.B. eben LoadFrom und SaveTo) zu haben, die nicht durch ein gemeinsames Interface normiert sind. Und da die RTL schon ein solches Interface bereitstellt, fiel die Entscheidung noch einmal leichter.


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:50 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-2025 by Thomas Breitkreuz