|
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();
}
|