Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Prism Record zu Array of Byte (https://www.delphipraxis.net/59046-record-zu-array-byte.html)

DerSascha 16. Dez 2005 15:09


Record zu Array of Byte
 
Hallo,

weiss jemand wie ich in .NET ein Record in ein Array of Byte konvertiere ohne über solche Verrenkungen wie BinaryReader/Writer gehen zu müssen?

Delphi-Quellcode:
  TBitmapCoreHeader = record                                        // 14 Bytes
    Signature      :UInt16; // 'BM'
    FileSize       :UInt32; // Dateigröße in Bytes
    Reserved       :UInt32; // Unbenutzt
    DataOffset     :UInt32; // Datenaufsatzpunkt (Offset)
  end;
Nun hab ich da ein gefülltes Record FBitmapCoreHeader und möchte das als einfaches Array of Byte (TBytes) haben.

Habe sowas wie StructureToBytes(TObject(FBitmapCoreHeader)) probiert, aber das ist plattformspezifisch und schmeißt zudem
eine Exception ;-).

Gruss,
Sascha

brechi 16. Dez 2005 15:30

Re: Record zu Array of Byte
 
Delphi-Quellcode:
type
  uint16 = word;
  uint32 = dword;

 TBitmapCoreHeader = record                                        // 14 Bytes
    Signature      :UInt16; // 'BM'
    FileSize       :UInt32; // Dateigröße in Bytes
    Reserved       :UInt32; // Unbenutzt
    DataOffset     :UInt32; // Datenaufsatzpunkt (Offset)
  end;
  TBarray = array[0..sizeOf(TBitmapCoreHeader)-1] of byte;
  PBarray = ^TBarray;



procedure TForm1.FormCreate(Sender: TObject);
var x: TBitmapCoreHeader;
    y: PBArray;
begin
  y := @x;
??

DerSascha 16. Dez 2005 15:45

Re: Record zu Array of Byte
 
Hallo Brechi,

Danke für deine Antwort. Leider scheint das unter .NET nicht zu funktionieren. Der Compiler meldet:

[Fehler] bmp.pas(221): E2410 Unsichere Zeigervariablen, Parameter oder Konstanten sind nur in unsicheren Prozeduren zulässig
[Fehler] bmp.pas(223): E2396 Unsicherer Code ist nur in unsicheren Prozeduren zulässig

Das mit Zeigern ist für .NET unsicher und geht scheinbar nur im Zusammenhang mit unmanaged Code AFAIK.

Gruss,
Sascha

Der_Unwissende 16. Dez 2005 15:54

Re: Record zu Array of Byte
 
Hi,
bin mir zwar nicht ganz sicher, aber würde mich doch sehr sehr stark wundern wenn in .Net Arrays nicht ohnehin als Zeiger übergeben werden. Wenn du also ein Array hast, wirst du so oder so kein Zeiger brauchen. Aber insbesondere aus Gründen der Typsicherheit wird .Net schon klug nur mit Pointern/Referenzen arbeiten und nicht wild Speicher kopieren, aber halt automatisch.
Was aber das eigentliche Problem angeht, so ist das von brechi gepostete Array schon passend.

Gruß Der Unwissende

DerSascha 16. Dez 2005 16:26

Re: Record zu Array of Byte
 
Hallo,

Zitat:

bin mir zwar nicht ganz sicher, aber würde mich doch sehr sehr stark wundern wenn in .Net Arrays nicht ohnehin als Zeiger übergeben werden. Wenn du also ein Array hast
Wo ist der Zusammenhang? Ich möchte ja gerade ein Array of Byte haben aus einer Struktur (einem Record).

Zitat:

Was aber das eigentliche Problem angeht, so ist das von brechi gepostete Array schon passend.
Öh, wie gesagt das funktioniert nicht. Ich glaube mittlerweile, dass ich das ganze über die Klasse System.Reflection
machen muß. Nur ist mir noch nicht ganz klar wie :-).

Gruss,
Sascha

Khabarakh 16. Dez 2005 18:37

Re: Record zu Array of Byte
 
Statt eine Methode mit "unsafe" zu kennzeichnen, kannst du auf Marshaller ausweichen.
Irgendwo aus dem Netz gefischt:
Code:
static byte[] YourStructToBytes( test s )
{
 int size = Marshal.SizeOf( s );
 byte[] retArr = new byte[ size ];
 IntPtr buf = Marshal.AllocHGlobal( size );   // create unmanaged memory
 Marshal.StructureToPtr ( s, buf, false );       // copy struct

 for (int i=0; i<size; ++i)
{
  retArr[i] = Marshal.ReadByte(buf, i);       // read unmanaged bytes
}
Marshal.FreeHGlobal( buf );
return retArr;
}

DerSascha 16. Dez 2005 18:45

Re: Record zu Array of Byte
 
Hallo Sebastian,

das sieht wirklich gut aus. Ich werde das am Montag gleich ausprobieren. Ich mach jetzt aber erst mal Feierabend/WE. Ich glaube ich bekomme das mit Hilfe der System.Reflection auch noch hin. Ich schau dann mal was schneller geht. Ich denke es wird dein "gefischte" ;-) Beitrag sein. Reflection soll schnarchlangsam sein.

Danke,
Sascha

jbg 16. Dez 2005 21:19

Re: Record zu Array of Byte
 
Das ist doch ein wenig unperformant, wenn man für jedes Byte den Marshaller anwerfen muss.

Code:
int size = Marshal.SizeOf(myStruct);
byte[] retArr = new byte[size];
IntPtr p = Marshal.AllocHGlobal(size);
try {
  Marshal.StructureToPtr(myStruct, p, false);
  Marshal.Copy(p, retArr, size);

finally {
  Marshal.FreeHGlobal(p);
}
return retArr;

DerSascha 19. Dez 2005 18:51

Re: Record zu Array of Byte
 
Hallo,

vielen Dank noch mal an alle. Die Konvertierung funktioniert nun wunderbar :-).

So funktioniert die Konvertierung von (packed) Records in TBytes (Array of Byte). Das ist die 1:1-Umsetzung von Adreas Vorschlag. Die Aufrufparameter der Methode Copy mußten ein wenig angepaßt werden, weil es die Methode mit den Aufrufparametern so wohl nicht mehr existiert(?).
Delphi-Quellcode:
class function THalloele.convertStructureToBytes(
  astructure: System.Object):TBytes;
var
  groesse: Integer;
  intp: IntPtr;
begin
  groesse := Marshal.&SizeOf(astructure);
  SetLength(Result, groesse);
  intp := Marshal.AllocHGlobal(groesse);
  try
    Marshal.StructureToPtr(astructure, intp, false);
    Marshal.&Copy(intp, Result, 0, groesse);
  finally
    Marshal.FreeHGlobal(intp);
  end;
end;
Der Versuch über System.Reflection tut leider nicht und mir ist natürlich nicht klar warum, aber ich poste das hier troztdem ;-).
Delphi-Quellcode:
class function THalloele.reflect(struktur: System.Object):TBytes;
var
  ms  :MemoryStream;
  typ:   System.&Type;
  fields: Array of FieldInfo;
  i :Integer;
  value: TObject;
  puffer: TBytes;
begin
  Result := nil;

  ms := MemoryStream.Create();
  try
    try
      typ   := TypeOf(struktur);
      fields := struktur.GetType.GetFields;
      for i := Low(fields) to High(fields) do
      begin
        value := fields[i].GetValue(struktur);
        puffer := TBytes(value);
        ms.Write(puffer, 0, Marshal.&SizeOf(fields[i].FieldType));
      end;
      Result := ms.ToArray;
    except on e:Exception do
      begin
        e.Message;
      end;
    end;
  finally
    ms.Close;
  end;
end;
Was mir noch aufgefallen ist:
  • Records müssen packed sein und offenbar mit dem Attribut [StructLayout(LayoutKind.Sequential)] deklariert werden, damit sie ge"marshalled" werden können
  • Es scheint etwas mühsam die Größe eines Aufzählungstyps zu erzwingen: Mein Weg ist ${Z4} plus [MarshalAs(UnmanagedType.U4)] um ein "WrapperRecord" (Erzwingen einer Breite von hier z.B. 4 Byte. Für bessere Ideen hab ich immer ein offenes Ohr. Das sieht nämlich ziemlich unschön aus :-)

Delphi-Quellcode:
  {$Z4}
  TBitmapCompression = (BI_RGB = 0, BI_RLE8 = 1, BI_RLE4 = 2);

  [StructLayout(LayoutKind.Sequential)]
  TBitmapInfoheaderCompression = packed record
    [MarshalAs(UnmanagedType.U4)]
    CompressionType: TBitmapCompression;
  end;


  [StructLayout(LayoutKind.Sequential)]
  TBitmapCoreHeader = packed record                                 // 14 Bytes
    Signature      :UInt16; // 'BM'
    FileSize       :UInt32; // Dateigröße in Bytes
    Reserved       :UInt32; // Unbenutzt
    DataOffset     :UInt32; // Datenaufsatzpunkt (Offset)
  end;


  [StructLayout(LayoutKind.Sequential)]
  TBitmapInfoHeader = packed record                                 // 40 Bytes
    Size           :UInt32; // Größe des InfoHeader (fest 40)
    Width          :UInt32; // Bitmap-Breite
    Height         :UInt32; // Bitmap-Höhe
    Planes         :UInt16; // Anzahl der Ebenen/Planes (fest 1)
    BitCount       :UInt16; // Bits pro Pixel
                             //   1 = monochrome palette. NumColors = 1
                             //   4 = 4bit palletized. NumColors = 16
                             //   8 = 8bit palletized. NumColors = 256
                             //   16 = 16bit RGB. NumColors = 65536 (?)
                             //   24 = 24bit RGB. NumColors = 16M
    Compression    :TBitmapInfoheaderCompression; // Kompressionstyp
                             //   0 = BI_RGB  no compression
                             //   1 = BI_RLE8 8bit RLE encoding
                             //   2 = BI_RLE4 4bit RLE encoding
    ImageSize      :UInt32; // (compressed) Size of Image
                             //    It is valid to set this =0 if Compression = 0
    XpixelsPerM    :UInt32; // horizontal resolution: Pixels/meter
    YpixelsPerM    :UInt32; // vertical resolution: Pixels/meter
    ColorsUsed     :UInt32; // Zahl der tatsächlich verwendeten Farben
    ColorsImportant :UInt32; // Anzahl der wichtigen Farben
                             //    0 = all
  end;
Vielen Dank,
Sascha


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