URL dieses Artikels:

Delphi 7-Struktur mit C# auswerten
Frage: Wie kann ich eine C-Struktur in C# definieren, damit ich einen Record mit fester Länge einlesen und auf die einzelnen Strukturelemente zugreifen kann?

Antwort: Ich habe nur ein Delphi-Beispiel für einen packed record zur Hand, dessen Inhalt über einen File of Record in einer Datei gespeichert wurde. Angenommen, ein "altes" Win32-Delphi-Programm hat die folgende strukturierte Datendatei geschrieben. Die Datenstruktur wurde als packed deklariert, so dass der Compiler die Ausrichtung nicht optimiert, sondern die Teile fortlaufend aneinanderkettet. Zum Test schreibt das Programm einen Datensatz in eine Datei:
type
  TPackedRec = packed record
   ID    : Integer;
   Name  : String[15];
   SI    : Shortint;
   Kat   : Byte;
   Rem   : String[10];
 end;

procedure TForm1.Button1Click(Sender: TObject); var aRec : TPackedRec; f : File Of TPackedRec; begin aRec.ID := 1; aRec.Name := 'ABCDEFGHIJKLMNO'; aRec.SI := 2; aRec.Kat := 3; aRec.Rem := '1234567890'; AssignFile(f, 'C:\Temp\D7.dat'); {-} Rewrite(f); {+} Write(f,aRec); CloseFile(f); end;
Um den binären Inhalt dieser Datei einzulesen und im C#-Programm verfügbar zu machen, muss das Speicherabbild des Delphi-Records explizit in C# deklariert werden. Dazu dient das StructLayout-Attribut (siehe System.Runtime.InteropServices), um bei der Hilfsstruktur die Daten aus der Datei in den Arbeitsspeicher umkopieren zu können. Um die feste Zeichenlänge bei den Strings zu berücksichtigten, wird das Attribut MarshalAs(UnmanagedType.ByValArray,SizeConst=xyz) genutzt, um ein char-Array der richtigen Größe anzufordern. Wenn die Daten dann mit Hilfe der Marshal-Klasse aus der Datei extrahiert wurden, können diese in die "normale" C#-Struktur umkopiert werden, so dass die Zeichenketten als Strings nutzbar sind.
/*
 Speicherabbild der Packed Record-Struktur von Delphi für die .NET-Welt 
 deklarieren (System.Runtime.InteropServices über using einbinden).
 Zusätzlich zu den String-Einträgen wird jeweils ein Hilfseintrag für die
 Stringlänge benötigt. Die Strings werden als char-Array abgelegt. 
*/

[StructLayout(LayoutKind.Sequential,Pack=0,CharSet=CharSet.Ansi,Size=34)] private struct PackedRecNative { internal Int32 ID; internal byte NameLen; [MarshalAs(UnmanagedType.ByValArray,SizeConst=15)] internal char[] Name; internal sbyte SI; internal byte Kat; internal byte RemLen; [MarshalAs(UnmanagedType.ByValArray,SizeConst=10)] internal char[] Rem; }
/* Struktur für die Nutzdaten aus der Datei */
public struct PackedRec { public int ID; public string Name; public sbyte SI; public byte Kat; public string Rem; }
private PackedRec ReadRecordData(System.IO.Stream s) { PackedRecNative aRawData; int iRawDataSize = Marshal.SizeOf(typeof(PackedRecNative)); byte[] aData = new byte[iRawDataSize]; s.Read(aData, 0, iRawDataSize); IntPtr aBuffer = Marshal.AllocHGlobal(iRawDataSize); try { Marshal.Copy(aData, 0, aBuffer, iRawDataSize); aRawData = (PackedRecNative)Marshal.PtrToStructure(aBuffer, typeof(PackedRecNative)); } finally { Marshal.FreeHGlobal(aBuffer); } PackedRec aRec = new PackedRec(); aRec.ID = aRawData.ID; aRec.Name = new String(aRawData.Name, 0, aRawData.NameLen); aRec.SI = aRawData.SI; aRec.Kat= aRawData.Kat; aRec.Rem = new String(aRawData.Rem, 0, aRawData.RemLen); return aRec; }
private void ReadFile() { System.IO.FileStream fs = new System.IO.FileStream(@"c:\Temp\D7.dat", System.IO.FileMode.Open); while (fs.Position < fs.Length) { PackedRec r = ReadRecordData(fs); MessageBox.Show(r.Name, r.Rem); } }
private void button1_Click(object sender, System.EventArgs e) { ReadFile(); }

© 2002 Software & Support Verlag GmbH. Vervielfältigung nur mit Genehmigung des Verlags. Alle Markennamen sind in der Regel eingetragene Warenzeichen der entsprechenden Unternehmen oder Organisationen.
Fragen? ... zum Angebot des Software & Support Verlags: info@entwickler.com
... zu dieser Website: webmaster@entwickler.com