Special INI Lib v1.0

    • Release

    Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

      Special INI Lib v1.0

      SpecialINILib

      Beschreibung:
      Direkt von Vornherein: Das ist keine "normale" 0815-INI Lib! Häufig hat man diverse Einstellungen, die das eigene Programm in Funktion und Aussehen beeinflussen (=> Farben, zahlreiche "Trigger" - also Boolean-Variablen, Zahlenwerte, etc). Natürlich gibt es tausend (bessere) Wege diese zu speichern und zu laden (serialisieren, my.settings,...) als auf eine INI-Datei zurückzugreifen - manchmal tut man das aber doch, nur um sich an die "alten Zeiten" zu erinnern xD Und mal ehrlich: INI-Dateien sind schon praktisch um dem fortgeschrittenen Nutzer mehr Freiheiten zu geben.

      Wie auch immer, wer sich für das INI-System entscheidet hat gleich viele Probleme - Das einlesen birgt so einige Hürden: statt einer Zahl wird "haxx0r" einem Schlüssel zugeordnet, das Programm muss auf diese Ausnahme reagieren. Bei mehreren Einstellungen verliert der Programmierer schnell die Lust, da er jedes Paar einzeln behandeln muss... genau da setzt meine Lib an!

      Wie ich schon irgendwo geschrieben habe: Jede Instanz, ganz gleich welchen Types, kann mit der Lib verarbeitet werden. Einzige Voraussetzung: Sie enthält Properties (=> siehe unterstützte Datentypen). Die Lib "klinkt" sich per Extension an Object ein und ist somit Teil jedes Datentyps in .NET

      Benutzung:
      Der Nachteil meiner Lib ist, dass man für seine Einstellungen zwangsläufig eine Klasse (mit entsprechenden Properties) erstellen muss. Das ist aber gleichzeitig der große Vorteil: Man hat während der Entwicklungszeit die Unterstütztung von IntelliSense und muss sich nicht mit Konstrukten wie "cint(INI_Read("Sektion","IeinSchlüssel"))" rummschlagen. Ebenfalls braucht man sich nicht selbst um die TypUnwandlungen kümmern - die Varibalen in der Klasse haben ja ihren fest zugeordneten Typ!
      Meine Lib übernimmt das Speichern und Laden der Einstellungen in und aus einer INI-Datei - und das vollautomatisiert:

      Zu Beginn steht:

      VB.NET-Quellcode

      1. Imports SpecialINILib.PropertyToINI


      Als erstes benötigt man eine Klasse welche die Einstellungen "kapselt"
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Public Class MeineTesteinstellungen
      2. Dim _bool As Boolean = False
      3. Dim _str As String = "Hier steht etwas..."
      4. Dim _Color As Color = Color.Black
      5. Dim _zahl As Integer = 42
      6. Public Property EinBoolean() As Boolean
      7. Get
      8. Return _bool
      9. End Get
      10. Set(ByVal value As Boolean)
      11. _bool = value
      12. End Set
      13. End Property
      14. Public Property EinText() As String
      15. Get
      16. Return _str
      17. End Get
      18. Set(ByVal value As String)
      19. _str = value
      20. End Set
      21. End Property
      22. <NoINIProperty()> Public Property EineZahl() As Integer
      23. Get
      24. Return _zahl
      25. End Get
      26. Set(ByVal value As Integer)
      27. _zahl = value
      28. End Set
      29. End Property
      30. Public Property EineFarbe() As Color
      31. Get
      32. Return _Color
      33. End Get
      34. Set(ByVal value As Color)
      35. _Color = value
      36. End Set
      37. End Property
      38. End Class



      Speichern in eine INI-Datei
      Diese Einstellungen können automatisiert in eine Sektion gespeichert werden (im tmpINI_Datei String befindet sich der Inhalt der INI-Sektion. Wie dieser in eine Datei geschrieben wird bzw ausgelesen wird soll Problem des Programmieres sein^^)
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Dim Einstellungen As New MeineTesteinstellungen
      2. '..Einstellungen verändern sich...
      3. With Einstellungen
      4. .EinBoolean = True
      5. .EineFarbe = Color.Red
      6. .EineZahl = 13
      7. .EinText = "Mal schauen ob es klappt.."
      8. End With
      9. Dim tmpINI_Datei As String = ""'<= Das ist unsere "INI-Datei". Der String muss natürlich in eine echte Datei geschrieben/gelesen werden!
      10. tmpINI_Datei = Einstellungen.ExportINISettings("TestSektion", True) '=> ALLE (möglichen) Properties werden in die INI geschrieben
      11. 'tmpINI_Datei = Einstellungen.ExportINISettings("TestSektion", False) ' => nur per Attribut "markierte" Properties werden (wenn möglich) in die INI geschreiben


      Laden aus einer INI-Datei
      Mit diesem "Einzeiler" kann man die INI-Datei - unter Angabe einer Sektion - einlesen
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Dim Einstellungen As New MeineTesteinstellungen
      2. Einstellungen.ImportINISettings(tmpINI_Datei, "TestSektion")



      Für den Fall, dass die EinstellungenKlasse Properties enthält, die nicht in die INI-Datei gelangen sollen ist auch vorgesorgt - für den umgekehrten Fall, dass nur einige wenige gespeichert und geladen werden sollen ebenfalls:
      Mit den Attributen <INIProperty()> und <NoINIProperty()> kann man genau festlegen welche Properties in die INI-File geschrieben werden dürfen. Eine Überladung von <INIProperty()> ermöglicht es darüber hinaus einen Hinweis (per Kommentar) in die INI-Datei schreiben zu lassen. (Tipp: man kann eine Klasse per Attribut markieren. Tipp2: Die Markierungen sind nicht erforderlich, jede Klasse die gültige Variablen enthält kann ihre Einstellungen in das INI-Format bringen und umgekehrt)



      Wie man sieht, werden nur "markierte" Properties gespeichert. Un-Markierte bzw als NoINIProperty markierte werden nicht gespeichert. Diesen Mechanismus kann man beeinflussen - .ExportINISettings hat mehrere Überladungen. Schaltet man AllPossibleProperties auf true, so werden alle (möglichen) Properties gespeichert, egal ob sie markiert sind oder nicht. Nur NoINIProperty-Markierte entkommen dem.

      Unterstütze Datentypen:
      Boolean, String, Color, Rectangle, Point, Date
      Zahlenwerte: Integer, Single, Decimal, Double, Byte, Long

      Hinweis:
      INI-Dateien sind natürlich sehr Fehleranfällig (bsp: "Haxx0r" statt Zahlenwert). Diese "Lesefehler" werden einfach übergangen, d.h. der Wert der betreffenden Variable bleibt unverändert. Auf Userwunsch (SpecialINILib.ThrowReadErrors = True) wird in solchen Fällen eine Exception geworfen. Aus diesem Grund ist davon abzuraten INI-Dateien als "Dateiformat" zu missbrauchen denn bei Programmeinstellungen (ob AntiAliasing verwendet wird oder nicht, etc) ist die Tragweite solcher fehlinterpretationen eher gering.

      Verwendete Programmiersprache:
      Visual Basic .NET (IDE: VB 2008 Express)

      Systemanforderungen:
      .NET Framework 3.5

      Lizenz/Weitergabe:
      Freeware, Namensnennung wäre nett^^
      Dateien

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „FreakJNS“ ()

      Nach Wunsch gibts hier den Source^^

      Spoiler anzeigen

      VB.NET-Quellcode

      1. Imports System.ComponentModel
      2. Imports System.drawing
      3. Imports System.Runtime.CompilerServices
      4. Public Module PropertyToINI
      5. Public CommentsPräfix As Char = ";"c 'Präfix für eine Kommentar-zeile
      6. Public ThrowReadErrors As Boolean = False 'Soll eine Exception geworfen werden, wenn ein Schlüssel-Wert-Paar nicht eingelesen werden kann.
      7. <Extension()> Function ExportINISettings(ByVal obj As Object, ByVal Sektion As String, Optional ByVal AllPossibleProperties As Boolean = False) As String
      8. Dim x() As Reflection.PropertyInfo = obj.GetType.GetProperties
      9. Dim tmp As String = "[" & Sektion & "]" & vbCrLf
      10. For Each pi As Reflection.PropertyInfo In x
      11. Dim attr() As Object = pi.GetCustomAttributes(True)
      12. Dim isNoINIProperty As Boolean = False : If attr.Contains(New NoINIPropertyAttribute) Or obj.GetType.GetCustomAttributes(True).Contains(New NoINIPropertyAttribute) Then isNoINIProperty = True
      13. Dim isINIProperty As Boolean = False : If (attr.Contains(New INIPropertyAttribute) Or obj.GetType.GetCustomAttributes(True).Contains(New INIPropertyAttribute)) Then isINIProperty = True
      14. If (Not isNoINIProperty) And (AllPossibleProperties Or isINIProperty) Then
      15. 'Name und Wert bestimmen...
      16. Dim n As String = pi.Name
      17. Dim vo As Object = pi.GetValue(obj, Nothing)
      18. 'Beschreibung abrufen...
      19. Dim desc As String = ""
      20. For Each a As Object In attr
      21. If a.GetType Is GetType(INIPropertyAttribute) Then
      22. Dim ca As INIPropertyAttribute = DirectCast(a, INIPropertyAttribute)
      23. desc = Trim(ca.Description)
      24. End If
      25. Next
      26. 'Den Wert in einen String konvertieren, das Paar in den INI-String schreiben
      27. If vo IsNot Nothing Then
      28. Dim v As String = ValueToString(vo)
      29. If v IsNot Nothing Then
      30. If desc <> "" Then tmp &= CommentsPräfix & n & ": " & desc & vbCrLf
      31. tmp &= n & " = " & v & vbCrLf
      32. End If
      33. End If
      34. End If
      35. Next
      36. tmp &= vbCrLf
      37. Return tmp
      38. End Function
      39. <Extension()> Sub ImportINISettings(ByVal obj As Object, ByVal INIString As String, ByVal Sektion As String)
      40. Dim x() As Reflection.PropertyInfo = obj.GetType.GetProperties()
      41. Dim tmp As Dictionary(Of String, String) = GetSektionMembers(INIString, Sektion)
      42. If tmp IsNot Nothing Then
      43. For Each pi As Reflection.PropertyInfo In x
      44. If pi.CanWrite Then 'Wenn die Property beschrieben werden darf..
      45. Dim n As String = pi.Name
      46. Dim vo As String = Nothing
      47. Dim vType As Object = pi.GetValue(obj, Nothing) '
      48. If tmp.Keys.Contains(n) Then 'Liest den Wert zum Key aus der INI ein
      49. vo = tmp.Item(n)
      50. End If
      51. 'Den aus der INI ausgelesenen String in den entsprechenden Property-Typ konvertieren und zuweisen...
      52. If Not vo Is Nothing Then
      53. Try
      54. Select Case True
      55. Case vType.GetType Is GetType(Color) : pi.SetValue(obj, DirectCast((New ColorConverter).ConvertFromString(vo), Color), Nothing)
      56. Case vType.GetType Is GetType(Boolean) : pi.SetValue(obj, DirectCast((New BooleanConverter).ConvertFromInvariantString(vo), Boolean), Nothing)
      57. Case vType.GetType Is GetType(Rectangle) : pi.SetValue(obj, DirectCast((New RectangleConverter).ConvertFromInvariantString(vo), Rectangle), Nothing)
      58. Case vType.GetType Is GetType(Point) : pi.SetValue(obj, DirectCast((New PointConverter).ConvertFromInvariantString(vo), Point), Nothing)
      59. Case vType.GetType Is GetType(String) : pi.SetValue(obj, vo.ToString, Nothing)
      60. Case vType.GetType Is GetType(Date) : pi.SetValue(obj, Date.Parse(vo), Nothing)
      61. Case vType.GetType Is GetType(Integer) : pi.SetValue(obj, CInt(vo), Nothing)
      62. Case vType.GetType Is GetType(Single) : pi.SetValue(obj, CSng(vo), Nothing)
      63. Case vType.GetType Is GetType(Decimal) : pi.SetValue(obj, CDec(vo), Nothing)
      64. Case vType.GetType Is GetType(Double) : pi.SetValue(obj, CDbl(vo), Nothing)
      65. Case vType.GetType Is GetType(Byte) : pi.SetValue(obj, CSByte(vo), Nothing)
      66. Case vType.GetType Is GetType(Long) : pi.SetValue(obj, CLng(vo), Nothing)
      67. End Select
      68. Catch ex As Exception
      69. 'Kann ein wert nicht in den entsprechenden Typ konvertiert werden..
      70. If ThrowReadErrors Then Throw New Exception("Fehler beim Convertieren der Property: " & n)
      71. End Try
      72. End If
      73. End If
      74. Next
      75. End If
      76. End Sub
      77. 'Internes INI-System..
      78. Private Function GetSektionMembers(ByVal INI As String, ByVal Sektion As String) As Dictionary(Of String, String)
      79. Dim Sektions As Dictionary(Of String, Dictionary(Of String, String)) = ParseINI(INI)
      80. If Sektions.Keys.Contains(Sektion) Then
      81. Return Sektions(Sektion)
      82. Else
      83. Return Nothing
      84. End If
      85. End Function
      86. Private Function GetSektions(ByVal INI As String) As String()
      87. Dim Sektions As Dictionary(Of String, Dictionary(Of String, String)) = ParseINI(INI)
      88. Return Sektions.Keys.ToArray
      89. End Function
      90. Private Function ParseINI(ByVal INI As String) As Dictionary(Of String, Dictionary(Of String, String))
      91. Dim Sektions As New Dictionary(Of String, Dictionary(Of String, String))
      92. Dim tmpsektionName As String = ""
      93. For Each l As String In Split(INI, vbCrLf)
      94. Dim Line As String = Trim(l)
      95. If Line.Count > 0 Then
      96. If Line.First = "["c And Line.Last = "]"c Then
      97. tmpsektionName = Line
      98. tmpsektionName = tmpsektionName.Replace("["c, "")
      99. tmpsektionName = tmpsektionName.Replace("]"c, "")
      100. End If
      101. If l.Count > 0 AndAlso Not l.First = CommentsPräfix Then
      102. Dim splited() As String = Split(l, " = ", 2)
      103. If splited.Count = 2 Then
      104. Dim Key As String = splited(0)
      105. Dim Val As String = splited(1)
      106. If Sektions.Keys.Contains(tmpsektionName) Then
      107. If Not Sektions(tmpsektionName).Keys.Contains(Key) Then
      108. Sektions(tmpsektionName).Add(Key, Val)
      109. Else
      110. Sektions(tmpsektionName)(Key) = Val
      111. End If
      112. Else
      113. Sektions.Add(tmpsektionName, New Dictionary(Of String, String))
      114. Sektions(tmpsektionName).Add(Key, Val)
      115. End If
      116. End If
      117. End If
      118. End If
      119. Next
      120. Return Sektions
      121. End Function
      122. 'wandelt unterstützte Typen in Strings
      123. Private Function ValueToString(ByVal p As Object) As String
      124. Select Case True
      125. Case p.GetType Is GetType(String) : Return p.ToString
      126. Case p.GetType Is GetType(Color) : Return (New ColorConverter).ConvertToString(p)
      127. Case p.GetType Is GetType(Boolean) : Return p.ToString
      128. Case p.GetType Is GetType(Date) : Return p.ToString
      129. Case p.GetType Is GetType(Rectangle) : Return (New RectangleConverter).ConvertToInvariantString(p)
      130. Case p.GetType Is GetType(Point) : Return (New PointConverter).ConvertToInvariantString(p)
      131. Case IsNumeric(p.ToString) : Return p.ToString
      132. End Select
      133. Return Nothing
      134. End Function
      135. Public Class INIPropertyAttribute
      136. Inherits Attribute
      137. Public Sub New()
      138. End Sub
      139. Public Sub New(ByVal Beschreibung As String)
      140. Description = Beschreibung
      141. End Sub
      142. Public Description As String = ""
      143. Public Overrides Function Equals(ByVal obj As Object) As Boolean
      144. If TypeOf (obj) Is INIPropertyAttribute Then
      145. Return True
      146. Else
      147. Return False
      148. End If
      149. End Function
      150. End Class
      151. Public Class NoINIPropertyAttribute
      152. Inherits Attribute
      153. Public Sub New()
      154. End Sub
      155. End Class
      156. End Module


      (das ganze habe ich btw nicht mehr weiterentwicket und würde es in keinem eigenen Projekt mehr einsetzten wollen. Veraltet, teilweise nicht perfekt gecodet, etc. Zum Lernen aber ganz gut)
      Interessante Idee!
      Mfg: Gather
      Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gather“ ()

      --Entfernt--
      Mfg: Gather
      Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gather“ ()

      Wenn ich das richtig verstanden habe soll das so eine Art INISerializer sein?
      Dann hätte ich eine Frage wieso sollte man dies benutzen und nicht die vorhandenen, welche auch noch mehr Möglichkeiten bieten.
      z.B.: wieso Xml und nicht Binary: Weil man Xml noch anständig lesen kann
      oder wieso Binary und nicht Xml: weil Binary flexibler ist und mehr Datentypen kann.


      Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
      Was mir aufgefallen ist, da du sie schon Special nennst :P
      Und ich vermute mal Aufgrund der Möglichkeit zur Serialisierung:
      [VB.NET] IniLib - 08.11.2011

      da ist kein Special dabei und es wird auch Serialisierung unterstützt. Nunja, vlt. wird das Special gar nicht erwartet, weil ~blaze~ schon "Speziell" ist :D

      Wollte zwar keine korinthen kacken, habs jetzt aber trotzdem gemacht... :P
      Ich wollte auch mal ne total überflüssige Signatur:
      ---Leer---
      @thefiloe
      Richtig, ein stinknormaler Serializer kann mehr und ist besser. Das Teil hier verarbeitet aber automatisiert INI-Dateien - ich kenne keinen Serializer der Daten im INI-Format speichern/lesen kann - und zwar aus und in Klassen. Vorteil vom INI-Format ist, dass es sehr leicht von handzufuß bearbeitet werden kann, wer also seinen Usern mehr Einstellmöglichkeiten bieten will, dabei aber keinen workaround hinlegen will könnte auf das zurückgreifen. IMO ist es besser sowas zu verwenden als sich die Finger wundzuschreiben um INIs zu lesen/schreiben. Vor allem wenn man OptionStrict ON verwendet kann da jede Menge Code entstehen - fällt bei mir alles weg.

      @jvbsl
      Eine Speichern/Lade-Möglichkeit ist ja nicht alles, was meine Lib bietet^^ Der Punkt ist, dass meine Lib automatisiert Daten aus einer Klasse in eine INI-Datei schreibt bzw wieder ausliest. Man arbeitet also nichtmehr mit "GetValue("Sektion","Key")" oder dergleichen sonder mit etwas viel mächtigerem: InteliSense. Die Informationen liegen ja immer in einer Klasseninstanz vor und somit ist die Lib quasie ein Interface zwischen den rohen Ini-Einträgen und echten Klassen. Genau das ist der extreme Vorteil: Man kümmert sich vorranging nichtmehr um das Speichern/Laden (dass passiert weitestgehend automatisch), sondern kann das wesentliche (das Programm) programmieren^^

      Vergleiche mal vieviel Code nötig ist um nur 4 Einstellungen aus einer INI-Datei zu lesen bzw in sie zu schreiben - und dann schau dir das an:



      Die Einstellungen-Klasse kann beliebig viele Properties enthalten. Diese werden automatisch gelesen/geschrieben und stehen dann in einer Klasseninstanz zur verfügung und können mittels IntelliSense eingesehen werden. Wenn man nun einen INI-Eintrag hinzufügen will muss man nur eine Property adden. Im Save/Load-Part ändert sich garnichts! Besser kann man mit INI-Files nur arbeiten, wenn man sie nicht benutzt^^



      @BeefyX
      Da schause was? xD
      Das Try ist imo nötig, da es immer zu "falschen" Angaben kommen kann - INIs sind eben Fehleranfällig.
      Wenn du auf das "Select Case True" hinauswillst, dann kann ich versichern, dass es so seine Richtigkeit hat.


      lg^^
      Hi
      mein Programm unterstützt seit Version 2.3 auch Serialisierung, auch wenn die etwas komplizierter ist und vor Allem hierarchische Strukturen bildet, sodass auch Klassen in Klassen korrekt serialisiert werden (sollten).
      Wenn du Lust hast, kannst du sie oder den Source dir ja mal anschauen.

      Gruß
      ~blaze~