| 

.NET C# Java Javascript Exception

8
Hallo,

ich habe gerade damit begonnen, ein VB6 Programm auf VB2010 zu portieren, das Binär-Parameter in Dateien schreibt und auch wieder liest.

Dazu habe ich in VB6 Type-Variablen benutzt, was jetzt wohl Structure wäre.

Ein verkürztes Beispiel

Public Structure BinCP42
Dim set_code As Long
Dim channel_sel_Low As Byte
Dim name As string ' feste Länge 15 Zeichen
dim channel_s(15) As Byte
dim channel_link_s(17) As Byte
dim subchan_s(15, 15) As Byte
end structure


Für mein Programm brauche ich mehrere solche Structure-Variablen, die auch wesentlich umfangreicher sind.

In Vb6 war das ganz einfach.
Da hat man die Variablen definiert und gespeichert oder gelesen.

Wie packe ich das in VB2010 an ?

Habt Ihr Tipps für mich ?????

Danke!

hupsi
17.01.2013
hupsi 201 1 7
6 Antworten
4
Am besten machst Du aus der Structure eine Klasse und serialisierst die anschließend. Schau mal hier

In c# würde das ganze so aussehen:
[Serializable]
public class BinCP42
{
public long set_code { get; set; }
public byte channel_sel_Low { get; set; }
public string name { get; set; }
// usw. usw.

public void Save(string path)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, this);
stream.Close();
}

public static BinCP42 Load(string path)
{
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
BinCP42 obj = (BinCP42)formatter.Deserialize(stream);
stream.Close();
return obj;
}
}

// Erzeugen und speichern
BinCP42 b42 = new BinCP42();
b42.channel_sel_Low = 1;
b42.name = "Test";
b42.set_code = 100;
b42.Save("C:\temp\b42.bin");

// Wieder laden
BinCP42 b42Load = BinCP42.Load("C:\temp\b42.bin");


und per Konverter in vb.net
<Serializable> _
Public Class BinCP42
Public Property set_code() As Long
Get
Return m_set_code
End Get
Set
m_set_code = Value
End Set
End Property
Private m_set_code As Long
Public Property channel_sel_Low() As Byte
Get
Return m_channel_sel_Low
End Get
Set
m_channel_sel_Low = Value
End Set
End Property
Private m_channel_sel_Low As Byte
Public Property name() As String
Get
Return m_name
End Get
Set
m_name = Value
End Set
End Property
Private m_name As String
' usw. usw.

Public Sub Save(path As String)
Dim formatter As IFormatter = New BinaryFormatter()
Dim stream As Stream = New FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)
formatter.Serialize(stream, Me)
stream.Close()
End Sub

Public Shared Function Load(path As String) As BinCP42
Dim formatter As IFormatter = New BinaryFormatter()
Dim stream As Stream = New FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)
Dim obj As BinCP42 = DirectCast(formatter.Deserialize(stream), BinCP42)
stream.Close()
Return obj
End Function
End Class

' Erzeugen und speichern
Dim b42 As New BinCP42()
b42.channel_sel_Low = 1
b42.name = "Test"
b42.set_code = 100
b42.Save("C:\temp\b42.bin")

' Wieder laden
Dim b42Load As BinCP42 = BinCP42.Load("C:\temp\b42.bin")
17.01.2013
JEwen 2,7k 5
1
Bei der Serialisierung von Objekten muss man beachten, dass bei Änderungen an der Struktur der Klasse (z.B. Hinzufügen von Properties) alte serialisierte Objekte nicht mehr gelesen werden können. Es gibt aber einen Versionierungs-Mechanismus, mit dem man dieses Problem umgehen kann. Leider habe ich auf die schnelle kein adäquates Beispiel gefunden, folgender Link bietet aber einen Einstieg: http://msdn.microsoft.com/en-us/library/ms973893.aspx
luedi 17.01.2013
2
Wenn man Klassen serialisiert, dann aber bitte nie mit den Auto- Properties arbeiten: { get; set; }
Die kann man später u. U. nicht mehr deserialisieren. Siehe: http://codekicker.de/fragen/geht-binaerer-Serialisierung-Nachtraegliche-Aenderungen-Property
Xantiva 21.01.2013
2
@Xantiva: Das stimmt, sollte man nicht tun. Hatte ich hier aber auch nur gemacht um den Code möglichst kurz zu halten. Und es passiert ja wirklich nur dann, wenn man die Properties verändert.
JEwen 21.01.2013
4
Der Umweg über eine Klasse ist zwar möglich, hier aber nicht erforderlich. Strukturen besitzen gegenüber VB6 eine erweiterte Syntax und können selbst Methoden oder einen Konstruktor definieren. Strukturen können, anders als Klassen, aber nicht vererbt werden. Wichtig beim binären Lesen/Speichern ist, das die Strukturen binär speicherbar sind und eine feste Länge haben. Der folgende Code zeigt, wie die Datenstruktur definiert wird. Das Attribut kennzeichnet die Struktur als serialisierbar (speicherbar). Um Zeichenketten (Datentyp String) mit einer festen Länge zu deklarieren, die sich ausschließlich beim Speichern/Lesen auswirkt, wird das Attribut <vbFixedString()> unter Angabe der gewünschten, festen Zeichenkettenlänge genutzt. Datenfelder werden mit dem Attribut <VBFixedArray()> auf eine bestimmte Länge gebracht, wobei auch mehrere Dimensionen angegeben werden können. Allerdings werden Datenfelder nicht automatisch auf die entsprechende Größe gebracht. Dafür sorgt hier der Konstruktor New innerhalb der Datenstruktur. Dieser Konstruktor muß, dies ist eine Besonderheit (!), eine Parameterliste definieren, da New als Standardkonstruktor bereits definiert ist und nicht überschrieben werden kann. An dieser Stelle wird an den neuen Konstruktor ein Paramater als Wahrheitswert übergeben. Wird hier der Wert True übergeben, werden die Datenfelder direkt redimensioniert. Verzichtest Du darauf, musst Du die einzelnen Datenfeldelemente nach der Anlage einer neuen Strukturvariablen selbst redimensionieren (ist aber nicht sinnvoll). Damit ist die Datenstruktur bereits definiert.

<Serializable()>
Public Structure BinCP42
Dim set_code As Long
Dim channel_sel_Low As Byte
<VBFixedString(15)>
Dim name As String ' feste Länge 15 Zeichen
'Arrays nicht mit fester Größe in Strukturvariablen
<VBFixedArray(15)>
Dim channel_s() As Byte
<VBFixedArray(17)>
Dim channel_link_s() As Byte
<VBFixedArray(15, 15)>
Dim subchan_s(,) As Byte
Sub New(ByVal i As Boolean)
If i Then
ReDim channel_s(15)
ReDim channel_link_s(17)
ReDim subchan_s(15, 15)
End If
End Sub
End Structure


Die Methode SaveStructure2BinaryFile zeigt, wie eine Datenstruktur des neuen Typs BinCP42 binär in einer vorgegebenen Datei mit einem BinaryFormatter als Datenstrom binär abgespeichert beziehungaweise serialisiert wird. Die Datei ist hier fest vorgegeben und wird mit der Methode Serialize des BinaryFormatters gschrieben, um dann den Datenstrom wieder zu schließen.

Private Sub SaveStructure2BinaryFile(ByVal s As BinCP42)
Dim file As String = "C:\DATA.BIN"
Dim objFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
Dim fstream As New System.IO.FileStream(file, System.IO.FileMode.Create)
objFormatter.Serialize(fstream, s)
fstream.Close()
End Sub


Die Methode LoadBinaryFile2Structure liest eine so gesicherte Datenstruktur wieder ein. Der Name der Datenstruktur wird als Referenzparamater (!) übergeben. Nur so werden die eingelesenen Werte tatsächlich zurückgegeben. Das Lesen erfolgt ansonsten wieder über einen BinaryFormatter.

Private Sub LoadBinaryFile2Structure(ByRef s As BinCP42)
Dim file As String = "C:\DATA.BIN"
If System.IO.File.Exists(file) Then
Dim objFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
Dim fstream As New System.IO.FileStream(file, System.IO.FileMode.Open)
s = objFormatter.Deserialize(fstream)
fstream.Close()
End If
End Sub


Der nachfolgende Quelltext zeigt das Anlegen und Initialisieren einer neuen Strukturvariablen mit dem Namen values und dem Datentyp BinCP42, das Speichern sowie das Einlesen in die neue Strukturvariablen values2.

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim values As New BinCP42(True)
With values
.set_code = 123
.channel_sel_Low = 3
.name = "Test"
.channel_s(0) = 12
.channel_s(1) = 21
.channel_link_s(0) = 33
.subchan_s(0, 0) = 5
End With
'Datenstruktur speichern
SaveStructure2BinaryFile(values)
'und in neue Variable einlesen
Dim values2 As New BinCP42(True)
LoadBinaryFile2Structure(values2)
End Sub
17.01.2013
Claus M. 2,9k 9
Ich möchte darauf hinweisen, dass das VBFixedString Attibut ab .NET Framework 4.0 als Deprecated markiert ist.
luedi 18.01.2013
Sorry ich habe das Attribut mit dem Typ VB6.FixedLengthString verwechselt
luedi 18.01.2013
1
Hm, warum nicht, so geht es natürlich auch. Der 'Umweg' über die Klasse kommt mir dabei aber wesentlich einfacher, strukturierter und kürzer vor.
JEwen 18.01.2013
Hallo Claus,
danke für den Ansatz. Ich habe das Beispiel mal übernommen und schreibt tatsächlich Daten weg.

Das trifft nur nicht ganz meiner Vorstellung. Den Binärfile, den ich schreiben muß, ist ein fester Code, um ein Meßgerät zu programmieren, da kann ich die Variablennamen im File nicht gebrauchen. Es sollen einfach nur die VariablenWerte in der Reihenfolge geschrieben werden.
Wie mache ich das ? Oder muß ich einen anderen Ansatz wählen.
hupsi
hupsi 23.01.2013
0
Hallo
erstmal Danke für die ausführlichen Beispiele.
Die muss ich mir mal verinnerlichen.

Gruß
hupsi
17.01.2013
hupsi 201 1 7
0
Hier eine kurze Anmerkung zu JEwen und der Bevorzugung der Klassen:
Das ganze muss natürlich vor dem Hintergrund der Portierung eines vorhandenen VB6-Quelltextes betrachtet werden. Und dann ist es natürlich einfacher eine bestehende VB6-Struktur in eine neue VB.NET-Struktur umzuwandeln. Hat man die Besonderheiten einmal verstanden, lassen sich alle Strukturen nahezu unverändert übernehmen und es muss nicht detailliert objektorientiert gearbeitet werden (was man mit alten VB6-Strukturen ebenfalls nicht macht), hier müssen lediglich die Variablen neu instanziiert werden.

Und wenn es nicht um Portierungen vorhandener Quelltexte geht, kann man sich selbst entscheiden, welche Variante man für das Datenmanagement nutzt. Ich entscheide dies immer nach der Frage, ob Daten über Objekte vererbbar sein müssen und eine wesentliche Bedutung im Rahmen der OOP besteht. Bei der einfachen Datenverwaltung ist das Kodieren von Klassen zu aufwendig, sofern diese im Rahmen der Property-Prozeduren für die Dateneigenschaften nicht direkt gesonderte Arbeiten verrichten müssen.

Ansonsten noch ein paar Anmerkungen zu Deiner Lösung mit Klassen:
1) diese Berücksichtigen nicht die unterschiedlichen Dimensionen der Datenfelder
2) die Member-Variablen sind nirgendwo deklariert (z.B. m_name)
3) um die Quelltxtmenge zu reduzieren, sollte mit verkürzten Property-Prozeduren gearbeitet werden (hier werden ja nur Mitgliedsvariablen über Set gesetzt oder mit Get ausgelesen), dann kann auf die Member-Variablen verzichtet werden , also statt dem schreibintensiven

Public Property set_code() As Long
Get
Return m_set_code
End Get
Set
m_set_code = Value
End Set
End Property


besser das kompakte

Public Property set_code() As Long [= Standardwert]


wobei das Setzen auf einen Standardwert hier natürlich optional ist. Dabei gehe ich natürlich davon aus, das Properties nicht während dem Schreiben und Ändern verändert werden. Die verkürzte Variante hat im Übrigen keinen Einfluss auf den generierten Code, nur das der Compiler einem diese Standardarbeiten zur internen Datenveraltung anhand des vorgegenenem Quelltextes abnimmt (wann verkürzte Property-Prozeduren nicht nutzbar sind, ist übrigens in der .NET-Dokumantationausreichend dokumentiert).
23.01.2013
Claus M. 2,9k 9
0
Hallo hupsi,
bezogen auf die letzte Anmerkung zum benötigten binären Dateiformat:

Deine Fragestellung bezog sich zunächst ja nur auf die binäre Speicherung, ohne das das spezielle Format angeführt wurde. Jede Binärdatei kann man natürlich detailliert im Aufbau beeinflussen. Man kann Zusatzdaten einfügen, die Ausgabestrukturen beeinflussen, mehrere Datenstrukturen beliebig kombinieren und vieles mehr. Ebenso kann man Objekte, Kollektionen oder auch hierarchisch angeordnete Objektstrukturen binär sichern. VB.NET hat neben den binären Speicher- und Ladefunktionen per BinaryFormatter übrigens noch eigene Funktionen für die binäre Datenspeicherung, die sich im übrigen mehr an VB6 orientieren(siehe FileOpen, FilePut, FileGet und FileClose). Eigentlich sollte es damit kein Problem sein, sich an die erforderliche Datenstruktur eines bestimmten Meßgerätes zu halten, sofern das Format im Detail dokumentiert ist ;-). Vieleicht sollte man bei Binärdateien generell berücksichtigen, das alle darin enthaltenen Daten binär verschlüsselt abgelegt sind. Der BinaryFormatter ist etwas spezielles der abgelegte Binärdateien wieder formatiert (erkennbar am Namen). Die Funktion wird insbesondere für Objekte benötigt.

Eine weitere Alternative zum binären Speichern ist der BinaryWriter und zum binären Lesen von Daten ist der BinaryReader. Ein Beispiel dazu findet sich unter http://marco.seaside-graphics.de/programmierung/vb_net/vb-net-variablen-in-binar-dateien-speichern-auslese.
23.01.2013
Claus M. 2,9k 9
0
Hallo,

wen das interessiert. Ich habe die Lösung für mich gefunden.
Danke an alle die mir dabei geholfen haben.

Zwar das Beispiel von Claus, da habe ich die Save und Load
wie folgt abgeändert.

Module Module1
<Serializable()>
Public Structure BinCP42

Dim set_code As Int32 'Long
Dim channel_sel_Low As Byte
Dim channel_sel2_Low As Byte
<VBFixedString(15)>
Dim name As String ' feste Länge 15 Zeichen'Arrays nicht mit fester Größe in Strukturvariablen
<VBFixedArray(15)>
Dim channel_s() As Byte
<VBFixedArray(17)>
Dim channel_link_s() As Byte
<VBFixedArray(15, 15)>
Dim subchan_s(,) As Byte

Sub New(ByVal i As Boolean)
If i Then
ReDim channel_s(15)
ReDim channel_link_s(17)
ReDim subchan_s(15, 15)
End If
End Sub

End Structure



End Module

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

Dim values As New BinCP42(True)

With values
.set_code = 256
.channel_sel_Low = 5
.channel_sel2_Low = 6
.name = "Test"
.channel_s(0) = 12
.channel_s(1) = 21
.channel_link_s(0) = 33
.subchan_s(0, 0) = 5
End With

'Datenstruktur speichern
SaveStructure2BinaryFile(values)
End
'und in neue Variable einlesen
Dim values2 As New BinCP42(True)
LoadBinaryFile2Structure(values2)

End Sub

Private Sub SaveStructure2BinaryFile(ByVal s As BinCP42)
Dim file As String = "k:\mwa\DATA.BIN"

If IO.File.Exists(file) Then IO.File.Delete(file)

Dim ch As Integer = FreeFile()
FileOpen(ch, file, OpenMode.Binary)
FilePut(ch, s)
FileClose(ch)

End Sub

Private Sub LoadBinaryFile2Structure(ByRef s As BinCP42)
Dim file As String = "K:\mwa\DATA.BIN"
If System.IO.File.Exists(file) Then

Dim ch As Integer = FreeFile()
FileOpen(ch, file, OpenMode.Binary)
FileGet(ch, s)
FileClose(ch)

End If
End Sub
23.01.2013
hupsi 201 1 7

Stelle deine Variable-Frage jetzt!
TOP TECHNOLOGIES CONSULTING GmbH