Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   C# Memorystream vs TMemoryStream (https://www.delphipraxis.net/184724-memorystream-vs-tmemorystream.html)

EWeiss 16. Apr 2015 20:44

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:
                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);
C#
Code:
    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);
            }
          }
        }
      }
    }
Ich weis nicht wie ich den Part hier übergeben soll.
Zitat:

//CoverArtData.Data = ?;
Den Delphi teil kann ich nicht exakt übersetzen da es mit keinem Stream funktioniert.

gruss

Phoenix 16. Apr 2015 21:28

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:
using (BinaryReader br = new BinaryReader(BitmapStream))
{
    CoverArtData.Data = br.ReadBytes(s.Length);
}
Aber mal Grundsätzlich: Das, was Du da hast, ist nicht wirklich was, was man in C# so bauen würde.
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?

EWeiss 17. Apr 2015 04:59

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

Phoenix 17. Apr 2015 07:20

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:
  • 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.

EWeiss 17. Apr 2015 08:14

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:
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;
Von Delphi zu Delphi sollte das kein Problem sein.
Während das im .NET schon anders aussieht.

gruss

EWeiss 17. Apr 2015 09:17

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:

Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist.
wenn es langsam nicht hinhaut muss er selbst sehen wie er das macht. ;)

gruss

EWeiss 17. Apr 2015 10:18

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

Lemmy 17. Apr 2015 11:09

AW: Memorystream vs TMemoryStream
 
warum eigentlich nicht "einfach" MemoryMappedFiles verwenden?
https://msdn.microsoft.com/de-de/lib...vs.110%29.aspx

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...

EWeiss 17. Apr 2015 11:19

AW: Memorystream vs TMemoryStream
 
Zitat:

Zitat von Lemmy (Beitrag 1298182)
warum eigentlich nicht "einfach" MemoryMappedFiles verwenden?
https://msdn.microsoft.com/de-de/lib...vs.110%29.aspx

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...

Danke.
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

Lemmy 17. Apr 2015 11:35

AW: Memorystream vs TMemoryStream
 
Zitat:

Zitat von EWeiss (Beitrag 1298186)
So wie im Bild ersichtlich kommt auf der Delphi Seite nur Müll an.
Bin wohl damit überfordert :)

ist doch logisch.. das Objektmodell von Delphi entspricht halt nicht dem von .NET. Jemand der sich damit auskennt kann dir das erklären wo die Unterschiede in der Speicherverwaltung von Delphi und .net liegen (wo sind virtuelle Methoden definiert, wo die Eigenschaften....) wenn das nicht zusammen passt, dann kann bei der Interpretation auch nur Müll raus kommen.

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

Phoenix 17. Apr 2015 11:45

AW: Memorystream vs TMemoryStream
 
Uh. Es ist nahezu ein Ding der Unmöglichkeit, ein Struct in .NET so zu layouten, das die Datenstruktur so aussieht, wie sie bei Delphi aussieht.

Insbesondere, wenn Du hier Strings (die sind komplett inkompatibel, weil die in .NET immer immutable Objekte sind) und eben größere Binärdaten hin- und herschieben willst.

Nur ein Beispiel: In .NET ist ein String ein Objekt, und die Daten werden geshared. Will heissen, wenn Du 15 Strings hast, und 13 davon identischen Inhalt haben, hast Du im Endeffekt 15 Objekte (also eine Komplexe Datenstruktur), und 13 davon haben intern irgendwo einen Pointer auf den selben Datenbereich mit dem Inhalt des Strings und 2 zeigen woanders hin. Wenn Du den Inhalt eines der 13 Strings jetzt änderst, dann wird geschaut, ob es den Inhalt schonmal gibt, wenn ja, wird einfach nur der Pointer verbogen, wenn nein, wird der Inhalt halt irgendwo neu angelegt und dann der Zeiger darauf hin verbogen. Das .NET Managed ist, bekommst Du ohne ganz hartes natives Debugging auch nicht raus, an welcher Speicherstelle der String jetzt eigentlich wirklich liegt.

Das, was Du machen willst, involviert sehr viel unsafe Code.

Ich würde ernsthaft vorschlagen, die Herangehensweise zu überdenken. Es wäre besser, die ganze Funktionalität in einer komplett nativen Delphi-DLL zu verpacken, nur die Methoden zu exposen, und ausschliesslich primitive Datentypen zwischendrin auszutauschen. Oder, Du nimmst sowas wie Hydra, das Dir dann wirklich erlaubt komplette Interfaces zwischen .NET und Delphi hin- und her zu schieben, und das ganze Plugin-Framework kümmert sich für Dich dann um das ganze richtige Konvertieren etc.

EWeiss 17. Apr 2015 11:47

AW: Memorystream vs TMemoryStream
 
Zitat:

Zitat von Lemmy (Beitrag 1298190)
Zitat:

Zitat von EWeiss (Beitrag 1298186)
So wie im Bild ersichtlich kommt auf der Delphi Seite nur Müll an.
Bin wohl damit überfordert :)

ist doch logisch.. das Objektmodell von Delphi entspricht halt nicht dem von .NET. Jemand der sich damit auskennt kann dir das erklären wo die Unterschiede in der Speicherverwaltung von Delphi und .net liegen (wo sind virtuelle Methoden definiert, wo die Eigenschaften....) wenn das nicht zusammen passt, dann kann bei der Interpretation auch nur Müll raus kommen.

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

Das macht keinen Sinn sorry.
Ich habe selbst eine DLL in Delphi geschrieben und einen Wrapper dazu.
Bei mir ist es nur so das ich weis welche Dateien bzw. DataTypen in Delphi deklariert werden müssen
damit eine Zusammenarbeit beider Sprachen ohne große Probleme funktioniert.

Aber wie gesagt darauf habe ich kleinen Einfluss was oder wie er seine Daten verwaltet/übergibt.
Zudem macht es absolut keinen sinn irgendetwas in ZIP Dateien zu packen denn ich lese den TAG einer X-Beliebigen MP3 Datei aus
und die Bilddatei ist im Tag enthalten.
Soll ich das dann zippen ?

gruss

EWeiss 17. Apr 2015 11:50

AW: Memorystream vs TMemoryStream
 
Zitat:

Ich würde ernsthaft vorschlagen, die Herangehensweise zu überdenken. Es wäre besser, die ganze Funktionalität in einer komplett nativen Delphi-DLL zu verpacken, nur die Methoden zu exposen, und ausschliesslich primitive Datentypen zwischendrin auszutauschen. Oder, Du nimmst sowas wie Hydra, das Dir dann wirklich erlaubt komplette Interfaces zwischen .NET und Delphi hin- und her zu schieben, und das ganze Plugin-Framework kümmert sich für Dich dann um das ganze richtige Konvertieren etc.
Würde ich gerne machen wenn es denn meine DLL wäre.
NA ja die Strings hab ich nach IntPtr gecastet das ist kein Problem.
Das ich mit Strings keine Probleme habe kannst du sehen siehe Anhang.

Delphi-Quellcode:
type
    TCoverArtData = packed record
        Name: PWideChar;
        Data: Pointer;
        DataSize: Int64;
        Description: PWideChar;
        CoverType: DWord;
        MIMEType: PWideChar;
        PictureFormat: TTagPictureFormat;
        Width: DWord;
        Height: DWord;
        ColorDepth: DWord;
        NoOfColors: DWord;
        ID3v2TextEncoding: Integer;
        Index: Integer;
    end;
Code:
  [Serializable, StructLayout(LayoutKind.Sequential, Pack = 1)]
  public sealed class TCoverArtData
  {
    private IntPtr NameSPtr;
    public IntPtr Data;
    public long DataSize;
    private IntPtr DescriptionSPtr;
    public int CoverType;
    private IntPtr MIMETypeSPtr;
    public TTagPictureFormat PictureFormat;
    public int Width;
    public int Height;
    public int ColorDepth;
    public int NoOfColors;
    public int ID3v2TextEncoding;
    public int Index;

    public string Name
    {
      get
      {
        if (NameSPtr == IntPtr.Zero)
          return null;
        else
          return Marshal.PtrToStringAuto(NameSPtr);      
      }

      set
      { 
        NameSPtr = Marshal.StringToHGlobalAuto(value);
      }
    }

    public string Description
    {
      get
      {
        if (DescriptionSPtr == IntPtr.Zero)
          return null;
        else
          return Marshal.PtrToStringAuto(DescriptionSPtr);
      }

      set
      { 
        DescriptionSPtr = Marshal.StringToHGlobalAuto(value);
      }

    }

    public string MIMEType
    {
      get
      {
        if (MIMETypeSPtr == IntPtr.Zero)
          return null;
        else
          return Marshal.PtrToStringAuto(MIMETypeSPtr);
      }
      set
      { 
        MIMETypeSPtr = Marshal.StringToHGlobalAuto(value);
      }
    }

  }
gruss

Phoenix 17. Apr 2015 11:59

AW: Memorystream vs TMemoryStream
 
Zitat:

Zitat von EWeiss (Beitrag 1298193)
Das macht keinen Sinn sorry.

Ich habe selbst eine DLL in Delphi geschrieben und einen Wrapper dazu.
Bei mir ist es nur das das ich weis welche Dateien bzw. DataTypen in Delphi deklariert werden müssen
damit eine Zusammenarbeit beider Sprachen ohne große Probleme zurechtkommen.

Aber wie gesagt darauf habe ich kleinen Einfluss was oder wie er seine Daten verwaltet/übergibt.

Das macht keinen Sinn. Sorry.

Du sagst uns nicht, was Du genau machen willst bzw. musst.

Du sagst uns nicht, welcher Teil genau in Delphi ist und welcher genau in C#. Aktuell sieht es (zumindest für mich) NICHT danach aus, das Du eine DLL ansprechen willst. Es sieht für mich danach aus, als ob Du ein Stück Delphi-Code 1:1 nach C# portieren = duplizieren willst. Inklusive UI. Das hat mit Bibliotheken/DLL (einmal geschriebener Code, der eben nicht für andere Plattformen kopiert werden muss) ehrlich gesagt genau gar nichts zu tun.

Du sagst uns nicht, was der Delphi-Code machen soll, wie das Interface Deiner Delphi-DLL aussieht die Du vielleicht(? - siehe vorherigen Absatz) aus .NET ansprechen willst.

Du sagst uns auch nicht, wer der andere ist, der seine Daten (welche?) verwaltet und übergibt. Auch das ist für mich gerade Sinnbefreit, denn wenn Du eine DLL bereit stellst, definierst Du damit da genau die Schnittstelle, über die jemand anderes Daten an Dich übergibt. Eigentlich hast Du damit die 100%ige Kontrolle darüber, wie jemand seine Daten an Dich zu übergeben hat.

Ich kenne Deine Systeme und die Systemgrenzen nicht. Ich sehe doppelten udn nicht mehrfach verwendeten Code, und ich sehe das Du da zwei Plattformen hast, deren interne Datenstrukturen die Du direkt übergeben willst (sinnlos) grundlegend inkompatibel sind.

Sorry, aber um da wirklich Zielführend zu helfen brauch ich echt viel mehr Informationen.

Phoenix 17. Apr 2015 12:01

AW: Memorystream vs TMemoryStream
 
Zitat:

Zitat von EWeiss (Beitrag 1298193)
Ich habe selbst eine DLL in Delphi geschrieben und einen Wrapper dazu.

Zitat:

Zitat von EWeiss (Beitrag 1298196)
Würde ich gerne machen wenn es denn meine DLL wäre.

:freak: Ja was denn nun? Ist es Deine Dll, oder nicht? :glaskugel:

EWeiss 17. Apr 2015 12:04

AW: Memorystream vs TMemoryStream
 
Zitat:

Zitat von Phoenix (Beitrag 1298203)
Zitat:

Zitat von EWeiss (Beitrag 1298193)
Ich habe selbst eine DLL in Delphi geschrieben und einen Wrapper dazu.

Zitat:

Zitat von EWeiss (Beitrag 1298196)
Würde ich gerne machen wenn es denn meine DLL wäre.

:freak: Ja was denn nun? Ist es Deine Dll, oder nicht? :glaskugel:

Damit habe ich gesagt das ich selbst schon für meine eigene DLL einen Wrapper geschrieben habe und dort alles funktioniert.
Nein es ist nicht meine DLL.

Habe oben mal das Beispiel angehängt.
Ohne Quelltext.
Wenn nötig poste ich ihn gern.

gruss

EWeiss 17. Apr 2015 12:10

AW: Memorystream vs TMemoryStream
 
Zitat:

Du sagst uns nicht, welcher Teil genau in Delphi ist und welcher genau in C#. Aktuell sieht es (zumindest für mich) NICHT danach aus, das Du eine DLL ansprechen willst. Es sieht für mich danach aus, als ob Du ein Stück Delphi-Code 1:1 nach C# portieren = duplizieren willst. Inklusive UI. Das hat mit Bibliotheken/DLL (einmal geschriebener Code, der eben nicht für andere Plattformen kopiert werden muss) ehrlich gesagt genau gar nichts zu tun.
Ich übersetze gar nichts ;)
Das ist gar nicht nett von dir das du mir das unterstellst.. aber egal.
Ich habe die DLL (Wrapper für .NET) und die Anwendung in C# selbst geschrieben. (Nur nochmal zur Klarstellung) ;)
Den Quelltext kannst du im Archiv einsehen. (Aber nicht den der DLL (TagsLib.dll) nicht meine Arbeit)

Alles neu geschrieben außerdem würde eine Übersetzung keinen Sinn machen oder?
Der Delphi Auszug ist aus der API für Delphi Anwendungen die diese DLL verwenden können.
Alle anderen Delphi Quelltextauszüge sind von dem Delphi-Sample das für diese DLL (TagsLib.dll) geschrieben wurde.

Der Source.
http://home.arcor.de/weiss.em/Net_17_04_15.rar

gruss

Phoenix 17. Apr 2015 13:03

AW: Memorystream vs TMemoryStream
 
Ahhhhh. Jetzt wird ein Schuh draus :)

Okay, was ich jetzt sehe:

Du hast also diese TagsLib, die das Handling der Media-Metadaten übernimmt. I see.
Und Du hast ein GUI in C# (und VB.NET) gebaut, das als Beispiel herhalten soll, wie man diese Tagslib verwendet.

Musst Du unbedingt diese dll nutzen, oder kannst Du z.B. auch die Taglib-sharp verwenden, die das ganze auch rein in .NET abhandelt? https://github.com/mono/taglib-sharp/

Wenn Du unbedingt diese nutzen musst:
Das Ding nimmt einen IntPtr auf die Bilddaten. Das entspricht in der Tat am ehesten einem Byte[].
Siehe auch hier:

http://stackoverflow.com/questions/1...using-p-invoke

Also irgendwo ein Byte[] definieren, sicherstellen, das der Garbage collector den nicht wegräumt (wie bei SO beschrieben), und dann auf das Struct .Data den Pointer auf den Beginn des Byte[] legen. Das sollte dann eigentlich die Daten da rüberschaufeln.

EWeiss 17. Apr 2015 13:16

AW: Memorystream vs TMemoryStream
 
Zitat:

Musst Du unbedingt diese dll nutzen, oder kannst Du z.B. auch die Taglib-sharp verwenden, die das ganze auch rein in .NET abhandelt? https://github.com/mono/taglib-sharp/
Es ist ein Gefälligkeit Dienst.
Ich muss die DLL nicht benutzen aber ich möchte dem Entwickler helfen für sein Projekt
eine Möglichkeit zu schaffen das auch andere Developer Sprachen für diese DLL Unterstützt werden.

Für VB6 habe ich das Modul und die Unterstützung schon geschrieben.

Es ist eine reine Gefälligkeit ohne eigenen Nutzen.
Ich helfe halt gern wenn es denn möglich ist.

Zitat:

Das Ding nimmt einen IntPtr auf die Bilddaten. Das entspricht in der Tat am ehesten einem Byte[].
Siehe auch hier:
Das Problem ist die Struct. Irgendwie kommen total falsche Daten an wie im vorherigen Beitrag auf dem Bild zu sehen.
Selbst wenn ich CoverArtData.Data mit IntPtr.Zero übergeben würde dürfte der Aufruf nicht dazu führen.

Zitat:

Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist.
Allenfalls würde dann ein false zurückgeliefert ohne AV.

Zitat:

Und Du hast ein GUI in C# (und VB.NET) gebaut, das als Beispiel herhalten soll, wie man diese Tagslib verwendet.
Jo ;) Und die unmanaged DLL (TagsLibraryDefs.NET) die als reference eingebunden werden kann. (VB_NET und C#)

Hab im Moment keine Idee wie ich das regeln soll.
Hoffe das mir jemand da helfen kann der mehr Erfahrung hat.
Vielleicht findest du eine Lösung (bzw. kannst das beheben) für das eine Problem wenn du zeit hast natürlich :)

gruss

EWeiss 17. Apr 2015 15:49

AW: Memorystream vs TMemoryStream
 
Wenn ich das richtig verstanden habe sollte das korrekt sein.
Aber es kracht immer noch.

Code:
byte[] Data = new byte[BitmapStream.Length];
BitmapStream.Read(Data, 0, (int)BitmapStream.Length);

GCHandle DataHandle = GCHandle.Alloc(Data, GCHandleType.Pinned);
IntPtr address = DataHandle.AddrOfPinnedObject();

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 = address;
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);
}
DataHandle.Free();
Muss ich die struct selbst jetzt auch noch pinnen ?

Code:
GCHandle structHandle = GCHandle.Alloc(CoverArtData, GCHandleType.Pinned);
structHandle.Free()
gruss

EWeiss 18. Apr 2015 00:41

AW: Memorystream vs TMemoryStream
 
Funktioniert jetzt.

Den ganzen Kram mit dem Pinnedobject konnte ich mir sparen und ist nicht nötig.
Der einzige Nachteil habe jetzt einen unsicheren Code .. was ich eigentlich nicht wollte. (unsafe)
bedingt durch die Konvertierung vom Image zum Pointer.
Wenn jemand eine Idee hat wie ich das anders machen kann. Nur zu!
Möchte das unsafe gern wieder entfernen.

Code:
              byte[] openFile = File.ReadAllBytes(OpenFileDialog1.FileName);
              fixed (byte* p = openFile)
              {
                IntPtr image = (IntPtr)p;

                CoverArtData.Name = Marshal.StringToHGlobalAuto(Description);
                CoverArtData.CoverType = 3;
                CoverArtData.MIMEType = Marshal.StringToHGlobalAuto(MIMEType);
                CoverArtData.Description = Marshal.StringToHGlobalAuto(Description);
                CoverArtData.Width = Width;
                CoverArtData.Height = Height;
                CoverArtData.ColorDepth = ColorDepth;
                CoverArtData.NoOfColors = NoOfColors;
                CoverArtData.PictureFormat = CoverArtPictureFormat;
                CoverArtData.Data = image;
                CoverArtData.DataSize = BitmapStream.Length;

                if (TagsLib.TagsLibrary_AddCoverArt(Tags, TTagType.ttAutomatic, CoverArtData) == -1)
                {
                  MessageBox.Show("Error while adding cover art: ", OpenFileDialog1.FileName, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error);
                }
                image = IntPtr.Zero;

                BitmapStream.Close();
                btnSave_MouseDown(sender, e);
              }
gruss

OlafSt 18. Apr 2015 09:52

AW: Memorystream vs TMemoryStream
 
Das unsafe wirst du nicht wieder los, weil du schließlich Code produzierst, der unmanaged, also außerhalb der Kontrolle des .NET-RT liegt. Das wiederum liegt an der Delphi-DLL, die nun mal unmanaged ist.

EWeiss 18. Apr 2015 10:22

AW: Memorystream vs TMemoryStream
 
Zitat:

Zitat von OlafSt (Beitrag 1298295)
Das unsafe wirst du nicht wieder los, weil du schließlich Code produzierst, der unmanaged, also außerhalb der Kontrolle des .NET-RT liegt. Das wiederum liegt an der Delphi-DLL, die nun mal unmanaged ist.

In wie weit?
Wird nur verwendet wenn ich den Code mit dem flag unsafe kompiliere und unsafe explicit verwende.

Was hat das mit Delphi zu tun?
Wenn ich den code nicht mit unsicheren Pointern schreibe dürfte unsafe nicht aktiv sein.

unmanaged nicht gleich unsafe

gruss


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:53 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