Einzelnen Beitrag anzeigen

Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.611 Beiträge
 
#4

AW: Memorystream vs TMemoryStream

  Alt 17. Apr 2015, 07:20
Okay. Ich kann Dir nur sagen, das eine Bibliothek für .NET nicht genutzt werden wird, wenn sie sich nicht .NET-Like verhält. Und die tut das sicher nicht. Bei solchen Vorgaben würd ich kündigen. So nen Schmarrn würd ich nicht mitmachen.

Aber sei's drum. Du willst die Bild-Daten in CoverArtData.Data haben.

Das geht auf mehreren Wegen:
  • direkt das Image-Objekt zuweisen
  • die Daten des Streams in ein Byte Array zu lesen
  • direkt ein Stream-Objekt hier reinhängen

Das erste ist vermutlich das Eleganteste. Das Image-Objekt bietet direkt Zugriff auf die Bilddaten, und ist eh schon da, weil Du daraus Metadaten liest.

Das zweite geht vermutlich am einfachsten, Codeschnipsel habe ich Dir ja schon geschrieben. Von Delphi kannst Du das Byte[] lesen.

Mit System.IO.File.Open(filename) erzeugst Du einen FileStream. Der FileStream arbeitet komplett auf dem File system (daher der Name), und liest nur die Daten die Du aus dem Stream gerade anforderst von der Platte. Liest Du die gleichen Daten mehrfach aus, so geht das im Zweifel auch mehrfach auf die Platte. (Das OS mag da zwar cachen, aber das ist grundsätzlich ineffizient). Du musst die Daten also wenn Du sie in Memory haben willst, erstmal komplett von der Platte in Memory lesen, und dann am besten auch gleich das File-Handle schliessen.

Also hier der Vorschlag (pseudo-Code):
Code:
// Der ganze Block hier anstelle des einfachen öffnen des FileStreams machen:

// erst einen Memory-Stream erzeugen
var stream = new MemoryStream();

// datei öffnen
using (var fs = System.IO.File.Open(OpenFileDialog1.FileName, System.IO.FileMode.Open))
{  // daten komplett lesen
   fs.CopyTo(stream);
} // am Ende vom Using wird der FileStream und das File Handle automatisch disposed

stream.Position = 0; // memoryStream wieder an den Anfang setzen, sonst gibts nix zu lesen weil er vom Ende lesen will
Ab hier erstmal so weiter wie in Deinem Beispiel.
Du kannst den MemoryStream so wie er ist erstmal an Dein Objekt hängen.

Sobald Du die Daten an Delphi bzw. irgend was anderes unmanaged Zeugs übergeben willst, wird es noch einmal etwas komplizierter.

Schau Dir mal das hier an, insbesondere das Code-Sample am Ende:
https://msdn.microsoft.com/de-de/lib...vs.110%29.aspx

Was Du davon brauchst ist folgendes:

1.) Einen unverwalteten Speicherblock allokieren. Dazu muss die Länge der Daten bekannt sein.
2.) Einen UnmanagedMemoryStream erzeugen, der auf diesen unmanaged memory block verweist.

Dann musst Du im Prinzip nur MemoryStream.CopyTo(unmanageStream) machen. Nicht vergessen, Position vorher auf 0 zu setzen. Danach kannst Du den MemoryStream und den UnmanagedStream wieder disposen.

Jetzt hast Du die Daten in unverwaltetem Speicher. Du kannst nun einen Pointer auf diese Daten an Delphi übergeben, und Delphi sollte in der Lage sein, dort raus zu lesen. Hinterher nicht vergessen, den Speicherbereich wieder freizugeben (Marshal.FreeHGlobal).

Um noch einmal zu API-Design zurück zu kommen:

Ich sehe, Du musst eine Library portieren oder einen Wrapper für eine Library bauen. Das ist alles schön und gut. Ich möchte Dir noch etwas Argumentationshilfe für den Vollhonk mitgeben, der Dir diese "Anweisungen" gegeben hat:

Wodurch definiert sich eine Library? Genau, durch seine öffentliche API. Und *Nicht* durch ihre Implementation.
Solange zwei Libraries von aussen gleich aussehen und sich beim Benutzen gleich verhalten (kann durch Unit- und Integration-Tests sichergestellt werden), sind sie äquivalent.

Du kannst in dem Library die Aufgaben auf verschiedenste Arten lösen.
Das geht einmal Qualitativ hochwertig, und einmal hingerotzt. Vom UI direkt aufs Filesystem zuzugreifen, ist hingerotzt. Das hat man zwar mal vor einigen Dekaden bei Delphi von jemanden so gezeigt bekommen, aber das wird dadurch nicht richtiger. Vor allem nicht in Modernen Umgebungen.
Du solltest das File-Handling und das Auslesen der Metadaten in separate Klassen kapseln, und von einer weiteren Klasse aus mit dem UI verdrahten.

Dann erzeugst Du keinen Stream direkt aus dem File dialog, sondern liest im UI den File-Namen aus dem Dialog aus, der wird von der Steuerungsklasse aausgelesen und an die andere Klasse weitergegeben. Stichwort lose Kopplung.

Endergebnis? Exakt das gleiche. Du hast ne schön gefüllte CoverArtData klasse, mit der Du arbeiten kannst, aber noch dazu sauberen, testbaren Code. Und zwar am Ende des Tages mir weniger Aufwand, weil Du Dir durch die ganzen automatischen Tests den manuellen Testaufwand sparen kannst.
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat