![]() |
Memorystream vs TMemoryStream
Welche alternative gäbe es zu TMemoryStream?
Hab schon so vieles versucht aber alles führt nicht zu dem Ergebnis wie in Delphi. Delphi
Delphi-Quellcode:
PictureStream := TMemoryStream.Create;
try PictureStream.LoadFromFile(OpenDialog1.FileName); PictureStream.Seek(0, soBeginning); Description := ExtractFileName(OpenDialog1.FileName); PictureStream.Read(PictureMagic, 2); PictureStream.Seek(0, soBeginning); if PictureMagic = MAGIC_JPG then begin MIMEType := 'image/jpeg'; CoverArtPictureFormat := tpfJPEG; JPEGPicture := TJPEGImage.Create; try JPEGPicture.LoadFromStream(PictureStream); Width := JPEGPicture.Width; Height := JPEGPicture.Height; NoOfColors := 0; ColorDepth := 24; finally FreeAndNil(JPEGPicture); end; end;
Delphi-Quellcode:
C#
PictureStream.Seek(0, soBeginning);
//* Add the cover art CoverArtData.Name := PwideChar(Description); CoverArtData.CoverType := 3; //* ID3v2 cover type (3: front cover) CoverArtData.MIMEType := PwideChar(MIMEType); CoverArtData.Description := PwideChar(Description); CoverArtData.Width := Width; CoverArtData.Height := Height; CoverArtData.ColorDepth := ColorDepth; CoverArtData.NoOfColors := NoOfColors; CoverArtData.PictureFormat := CoverArtPictureFormat; CoverArtData.Data := PictureStream.Memory; CoverArtData.DataSize := PictureStream.Size; if TagsLibrary_AddCoverArt(Tags, ttAutomatic, CoverArtData) = - 1 then begin MessageDlg('Error while adding cover art: ' + SaveDialog1.FileName, mtError, [mbCancel], 0);
Code:
Ich weis nicht wie ich den Part hier übergeben soll.
private void btnAdd_MouseDown(object sender, MouseEventArgs e)
{ //* Clear the cover art data string MIMEType = ""; string Description = ""; int Width = 0; int Height = 0; int ColorDepth = 0; int NoOfColors = 0; TCoverArtData CoverArtData = new TCoverArtData(); TTagPictureFormat CoverArtPictureFormat = TTagPictureFormat.tpfUnknown; OpenFileDialog1.FileName = ""; OpenFileDialog1.Title = "Select a File..."; OpenFileDialog1.Filter = "Picture files (*.jpg*,*.jpeg*,*.bmp*,*.png*,*.gif*)|*.jpg*;*.jpeg*;*.bmp*;*.png*;*.gif*"; if (OpenFileDialog1.ShowDialog() == DialogResult.OK) { if (File.Exists(OpenFileDialog1.FileName)) { using (Stream BitmapStream = System.IO.File.Open(OpenFileDialog1.FileName, System.IO.FileMode.Open)) { Image img = Image.FromStream(BitmapStream); if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Jpeg)) { MIMEType = "image/jpeg"; CoverArtPictureFormat = TTagPictureFormat.tpfJPEG; Description = Path.GetFileName(OpenFileDialog1.FileName); Width = img.Width; Height = img.Height; NoOfColors = 0; ColorDepth = 24; } else if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Png)) { //TODO } else if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Gif)) { //TODO } else if (img.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Bmp)) { //TODO } CoverArtData.Name = Description; CoverArtData.CoverType = 3; CoverArtData.MIMEType = MIMEType; CoverArtData.Description = Description; CoverArtData.Width = Width; CoverArtData.Height = Height; CoverArtData.ColorDepth = ColorDepth; CoverArtData.NoOfColors = NoOfColors; CoverArtData.PictureFormat = CoverArtPictureFormat; //CoverArtData.Data = ?; CoverArtData.DataSize = BitmapStream.Length; if (TagsLib.TagsLibrary_AddCoverArt(Tags, TTagType.ttAutomatic, CoverArtData) != 0) { MessageBox.Show("Error while adding cover art: ", OpenFileDialog1.FileName, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error); } } } } } Zitat:
gruss |
AW: Memorystream vs TMemoryStream
Welcher Datentyp soll das denn sein bzw. werden?
Bzw, was willst Du eigentlich mit CoverArtData später machen? Streams in Memory rumschleppen ist eher doof. Wenn Data das eigentliche Bild enthalten soll, würde ich gleich das Image-Objekt was Du eh schon hast dort platzieren, und gar nicht lange rumfackeln. Wenn es Dir nur um die Daten geht, um sie z.B. später in eine Datenbank zu packen, würde ich ein Byte[] empfehlen. Das würdest Du dann so füllen:
Code:
Aber mal Grundsätzlich: Das, was Du da hast, ist nicht wirklich was, was man in C# so bauen würde.
using (BinaryReader br = new BinaryReader(BitmapStream))
{ CoverArtData.Data = br.ReadBytes(s.Length); } Es gibt so schöne Databinding-Ansätze in WPF, und man könnte das so schön Unit-Testbar bauen, das Laden und Auswerten der Image-Daten in eine separate Klasse auslagern (Single Responsibility Principle), etc. Was willst Du am Ende haben? |
AW: Memorystream vs TMemoryStream
Sorry geht nicht um die frage Warum sondern wie es umsetzbar ist.
Die Library ist in Delphi geschrieben und soll von unterschiedlichen Developer Sprachen Unterstützt werden. Frag mich also nicht warum der das so, So oder SO macht führt zu keinem Ergebnis weil ich keinen Einfluss darauf habe warum er es so macht. Aber mal ganz davon abgesehen wie soll er denn sonst an die Bild Daten gelangen wenn nicht aus dem Speicher. Diese befinden sich schließlich in Media Daten (TAGS) ala MP3 und Konsorten. Da die Daten eh im Speicher vorliegen warum dann nicht auch direkt nutzen nach dem der Tag ausgelesen wurde.. Warum also nicht C# verwenden um eine DLL zu erstellen die nachher von allen .NET Anwendungen als Reference eingebunden und verwendet werden können. In VB_NET macht es schwerlich sinn. Das ist nicht böse gemeint aber Fakt ist nun mal das es nichts bringt da ich mich an vorgaben halten muss. Ich schreibe einen Wrapper der zwischen Delphi C# und VB_NET die Daten hin und her schaufelt. Es nutz mir nichts zu hinterfragen warum er dies so oder so macht. Die Frage ist es machbar oder ist es nicht machbar. CoverArtData.Data ist die Bild Datei im Speicher. CoverArtData.Data := PictureStream.Memory; Der Pointer zur Bild Datei. Der Sinn soll sein die Daten nicht extra auf den Datenträgern ablegen zu müssen bevor sie verwendet werden. gruss |
AW: Memorystream vs TMemoryStream
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:
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:
Ab hier erstmal so weiter wie in Deinem Beispiel.
// 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 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: ![]() 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. |
AW: Memorystream vs TMemoryStream
Danke für deine Ausführung.. Werde das mal in ruhe durchgehen.
Hier nur noch als Info wie das im eigenen Sample von Delphi gehandhabt wird. Auf der Basis wollte ich das auch machen (was aber nun mal nicht geht mit den Streams in .NET)
Delphi-Quellcode:
Von Delphi zu Delphi sollte das kein Problem sein.
procedure TForm1.Button5Click(Sender: TObject);
var PictureStream: TMemoryStream; Description: String; MIMEType: String; JPEGPicture: TJPEGImage; PNGPicture: TPNGImage; GIFPicture: TGIFImage; BMPPicture: TBitmap; Width, Height: Integer; NoOfColors: Integer; ColorDepth: Integer; PictureMagic: Word; CoverArtPictureFormat: TTagPictureFormat; CoverArtData: TCoverArtData; begin if NOT OpenDialog1.Execute then begin Exit; end; //* Clear the cover art data MIMEType := ''; Description := ''; Width := 0; Height := 0; ColorDepth := 0; NoOfColors := 0; CoverArtPictureFormat := TTagPictureFormat.tpfUnknown; if FileExists(OpenDialog1.FileName) then begin try PictureStream := TMemoryStream.Create; try PictureStream.LoadFromFile(OpenDialog1.FileName); PictureStream.Seek(0, soBeginning); Description := ExtractFileName(OpenDialog1.FileName); PictureStream.Read(PictureMagic, 2); PictureStream.Seek(0, soBeginning); if PictureMagic = MAGIC_JPG then begin MIMEType := 'image/jpeg'; CoverArtPictureFormat := tpfJPEG; JPEGPicture := TJPEGImage.Create; try JPEGPicture.LoadFromStream(PictureStream); Width := JPEGPicture.Width; Height := JPEGPicture.Height; NoOfColors := 0; ColorDepth := 24; finally FreeAndNil(JPEGPicture); end; end; if PictureMagic = MAGIC_PNG then begin MIMEType := 'image/png'; CoverArtPictureFormat := tpfPNG; PNGPicture := TPNGImage.Create; try PNGPicture.LoadFromStream(PictureStream); Width := PNGPicture.Width; Height := PNGPicture.Height; NoOfColors := 0; ColorDepth := PNGPicture.PixelInformation.Header.BitDepth; finally FreeAndNil(PNGPicture); end; end; if PictureMagic = MAGIC_GIF then begin MIMEType := 'image/gif'; CoverArtPictureFormat := tpfGIF; GIFPicture := TGIFImage.Create; try GIFPicture.LoadFromStream(PictureStream); Width := GIFPicture.Width; Height := GIFPicture.Height; NoOfColors := 0; //GIFPicture.ColorResolution ColorDepth := GIFPicture.BitsPerPixel; finally FreeAndNil(GIFPicture); end; end; if PictureMagic = MAGIC_BMP then begin MIMEType := 'image/bmp'; CoverArtPictureFormat := tpfBMP; BMPPicture := TBitmap.Create; try BMPPicture.LoadFromStream(PictureStream); Width := BMPPicture.Width; Height := BMPPicture.Height; NoOfColors := 0; case BMPPicture.PixelFormat of pfDevice: ColorDepth := 32; pf1bit: ColorDepth := 1; pf4bit: ColorDepth := 4; pf8bit: ColorDepth := 8; pf15bit: ColorDepth := 15; pf16bit: ColorDepth := 16; pf24bit: ColorDepth := 24; pf32bit: ColorDepth := 32; pfCustom: ColorDepth := 32; end; finally FreeAndNil(BMPPicture); end; end; PictureStream.Seek(0, soBeginning); //* Add the cover art CoverArtData.Name := PwideChar(Description); CoverArtData.CoverType := 3; //* ID3v2 cover type (3: front cover) CoverArtData.MIMEType := PwideChar(MIMEType); CoverArtData.Description := PwideChar(Description); CoverArtData.Width := Width; CoverArtData.Height := Height; CoverArtData.ColorDepth := ColorDepth; CoverArtData.NoOfColors := NoOfColors; CoverArtData.PictureFormat := CoverArtPictureFormat; CoverArtData.Data := PictureStream.Memory; CoverArtData.DataSize := PictureStream.Size; if TagsLibrary_AddCoverArt(Tags, ttAutomatic, CoverArtData) = - 1 then begin MessageDlg('Error while adding cover art: ' + SaveDialog1.FileName, mtError, [mbCancel], 0); end; finally FreeAndNil(PictureStream); end; except MessageDlg('Error while adding cover art: ' + SaveDialog1.FileName, mtError, [mbCancel], 0); end; end; //* Display added cover art ListCoverArts; end; Während das im .NET schon anders aussieht. gruss |
AW: Memorystream vs TMemoryStream
Hab es jetzt mal so versucht direkt die Image Daten zu übergeben.
hab noch keinen UnmanagedMemoryStream erzeugt. Die Delphi Seite mag das anscheinend nicht.
Code:
IntPtr pval = IntPtr.Zero;
var BitmapStream = new MemoryStream(); using (var fs = System.IO.File.Open(OpenFileDialog1.FileName, System.IO.FileMode.Open)) { fs.CopyTo(BitmapStream); BitmapStream.Position = 0; Bitmap bmp = (Bitmap)Bitmap.FromStream(BitmapStream); System.Drawing.Imaging.BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); try { pval = bd.Scan0; } finally { bmp.UnlockBits(bd); } //.... CoverArtData.Data = pval; if (TagsLib.TagsLibrary_AddCoverArt(Tags, TTagType.ttAutomatic, CoverArtData) != 0) { MessageBox.Show("Error while adding cover art: ", OpenFileDialog1.FileName, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error); } Zitat:
gruss |
AW: Memorystream vs TMemoryStream
Der Müll kommt bei ihm an.
Deshalb kracht es. Kann mir da jemand helfen ? Bei bedarf häng ich das Projekt mal an. gruss |
AW: Memorystream vs TMemoryStream
warum eigentlich nicht "einfach" MemoryMappedFiles verwenden?
![]() sind doch genau dafür da.. Gut bisher habe ich da auch "nur" Daten zwischen Delphi-Prozessen ausgetauscht, aber damit sollte das doch problemlos auch nach .net gehen solange die komplette Bilddatei dort abgelegt wird... |
AW: Memorystream vs TMemoryStream
Zitat:
Es geht nicht alleine nur um die Bitmap die ich an CoverArtData.Data übergeben muss sondern um die struct TCoverArtData. So wie im Bild ersichtlich kommt auf der Delphi Seite nur Müll an. Bin wohl damit überfordert :) gruss |
AW: Memorystream vs TMemoryStream
Zitat:
Lösung: Das Objekt/Record in eine XML-Datei speichern, das Bitmap dazu (mime), ggf. zippen und die komplette XML dann ins MMF. Das kannst Du dann mit jeder bel. Programmiersprache auswerten, die MMF unterstützt... Nachtrag: Die Größe des MMF kannst Du dann per Windows-Message "herum reichen" auf die dann das andere Programm reagieren kann - das weiß dann ab wann die Infos komplett bereit stehen und auch wie groß die MMF ist in der die Daten stehen... Grüße |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:15 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