<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>verwalten Archive - Robert Skibbe</title>
	<atom:link href="https://robbelroot.de/blog/tag/verwalten/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>alias RobbelRoot – Freelance Full Stack Developer .NET</description>
	<lastBuildDate>Thu, 26 Mar 2026 06:50:23 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://robbelroot.de/wp-content/uploads/2020/12/cropped-favicon-32x32.png</url>
	<title>verwalten Archive - Robert Skibbe</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Ini Datei lesen und schreiben in VB.NET – Einstellungen speichern / laden</title>
		<link>https://robbelroot.de/blog/ini-datei-lesen-und-schreiben-einstellungen-speichern/</link>
					<comments>https://robbelroot.de/blog/ini-datei-lesen-und-schreiben-einstellungen-speichern/#comments</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Fri, 25 Jun 2021 13:42:43 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[Visual Basic .NET Problemlösungen]]></category>
		<category><![CDATA[datei]]></category>
		<category><![CDATA[einstellungen]]></category>
		<category><![CDATA[file]]></category>
		<category><![CDATA[files]]></category>
		<category><![CDATA[ini]]></category>
		<category><![CDATA[laden]]></category>
		<category><![CDATA[lesen]]></category>
		<category><![CDATA[persist]]></category>
		<category><![CDATA[persistent]]></category>
		<category><![CDATA[read]]></category>
		<category><![CDATA[schreiben]]></category>
		<category><![CDATA[settings]]></category>
		<category><![CDATA[speichern]]></category>
		<category><![CDATA[vb]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[verwalten]]></category>
		<category><![CDATA[write]]></category>
		<guid isPermaLink="false">http://robbelroot.de/?p=1003</guid>

					<description><![CDATA[<p>Ini Datei lesen und schreiben In diesem Beispiel zeige ich, wie man in VB.NET eine eine Ini Datei lesen und schreiben kann. Dadurch können wir im Anschluss Einstellungen für das Programm speichern und wieder laden. Ferner werden dazu Bestandteile aus der .NET API importiert und letztendlich verwendet, Diese sind allerdings &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/ini-datei-lesen-und-schreiben-einstellungen-speichern/">Ini Datei lesen und schreiben in VB.NET – Einstellungen speichern / laden</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Ini-Datei-lesen-und-schreiben-in-VB-NET-–-Einstellungen-speichern-und-laden.jpg"><img fetchpriority="high" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/Ini-Datei-lesen-und-schreiben-in-VB-NET-–-Einstellungen-speichern-und-laden.jpg" alt="Ini Datei lesen und schreiben in VB NET – Einstellungen speichern und laden" class="wp-image-9397" title="Ini Datei lesen und schreiben in VB NET – Einstellungen speichern und laden"/></a><figcaption class="wp-element-caption">Ini Datei lesen und schreiben VB.NET – Einstellungen speichern und laden</figcaption></figure>






<h2 class="wp-block-heading" id="ini-datei-lesen-und-schreiben">Ini Datei lesen und schreiben</h2>



<p>In diesem Beispiel zeige ich, wie man in VB.NET eine eine Ini Datei lesen und schreiben kann. Dadurch können wir im Anschluss Einstellungen für das Programm speichern und wieder laden. Ferner werden dazu Bestandteile aus der .NET API importiert und letztendlich verwendet, Diese sind allerdings optional und nur als weiteres Beispiel zu sehen.</p>



<p>Trotz des alten Flers haben Konfigurationsdateien auch heutzutage durchaus noch ihren Sinn, bzw. Ihre Daseinsberechtigung. Es ist zum Beispiel nicht immer gegeben, dass eine Datenbank, oder ähnliche Sachen zur Verfügung stehen.</p>



<h2 class="wp-block-heading" id="wie-du-schnell-starten-kannst">Schneller Weg: rskibbe.Ini NuGet-Paket</h2>



<p>Willst Du nicht viel fummeln und einfach sofort starten können? Dann <a href="https://robbelroot.de/blog/rskibbe-ini-parsing-reading-saving-ini-files-but-easy/"><strong>verwende mein NuGet-Paket</strong></a>, Welches Dir jegliche INI-Arbeit abnimmt &#8211; Beitrag besuchen, installieren, loslegen.</p>



<p>Möchtest Du alles selbst machen und einfach, schnell und <strong>objektorientiert </strong>arbeiten:</p>



<ul class="wp-block-list">
<li>Lade mein Beispielprojekt herunter</li>



<li>Kopiere die drei Klassen <strong><a href="#die-ini-klasse">Ini</a>, <a href="#die-section-klasse">Section</a>, <a href="#die-entry-klasse">Entry </a></strong>in dein Projekt</li>



<li>Lade eine Ini-Objekt mit der statischen (shared) &#8222;Ini.Load&#8220;-Methode</li>



<li>Füge Sektionen und Einträge mit den Methoden &#8222;AddSection&#8220; &amp; &#8222;AddEntry&#8220; hinzu</li>



<li>Speichere die Ini-Datei mit der &#8222;Save&#8220;-Methode der Ini-Klasse</li>



<li>Gehe für mehr Beispiele einfach <strong><a href="#eine-vorhandene-ini-datei-lesen">nach unten</a></strong></li>
</ul>



<p>Auch hier setzt Du alles selbst um und kannst die Windows-API Funktionen verwenden, gehe wie folgt vor:</p>



<ul class="wp-block-list">
<li>Zieh&#8216; Dir die API-Funktionsdeklaration namens &#8222;<strong><a href="#getprivateprofilestring-api-funktion">GetPrivateProfileString</a></strong>&#8222;</li>



<li>Hol&#8216; Dir ebenso die Deklaration zum Schreiben: &#8222;<strong><a href="#writeprivateprofilestring-api-funktion">WritePrivateProfileString</a></strong>&#8222;</li>



<li>Ruf die Methoden mit passenden Parametern auf – feddich</li>
</ul>



<h2 class="wp-block-heading" id="video-auf-youtube-ansehen">Video auf YouTube ansehen</h2>



<p>Hier findest du <strong>mein YouTube-Video</strong> zum Thema Einstellungen speichern und laden via Konfigurationsdatei in VB.NET.</p>


<div class="async-youtube" data-embed="hMQ8lmTv4n8" data-alt="">
    <div class="play-button"></div>      
  </div>



<h2 class="wp-block-heading" id="die-initialisierungsdatei-kurz-ini">Die Initialisierungsdatei – kurz Ini</h2>



<p>Die Initialisierungsdatei – kurz Ini – findet Ihren Ursprung in der Zeit vor der Windows-Registry.</p>



<blockquote class="wp-block-quote mb-2 is-layout-flow wp-block-quote-is-layout-flow">
<p>Bis zur Einführung der <a href="https://de.wikipedia.org/wiki/Registrierungsdatenbank">Registrierungsdatenbank</a> mit <a href="https://de.wikipedia.org/wiki/Microsoft_Windows_NT_3.1">Microsoft Windows NT 3.1</a> war das INI-Format das einzige <a href="https://de.wikipedia.org/wiki/Dateiformat">Dateiformat</a> zur Speicherung von Programm-Konfigurationen, das durch die <a href="https://de.wikipedia.org/wiki/WinAPI">WinAPI</a> unterstützt wurde.</p>
<cite><a href="https://de.wikipedia.org/wiki/Initialisierungsdatei" target="_blank" rel="noreferrer noopener nofollow">Wikipedia</a> – Initialisierungsdatei</cite></blockquote>



<h3 class="wp-block-heading" id="aufbau-der-ini">Aufbau der Ini</h3>



<p>Der Aufbau der Initialisierungsdatei ist ein relativ einfacher <strong>Zusammenbau aus Schlüssel- und Wert-Paaren</strong> (engl. Key-Value Pairs). Die Schlüssel-Wert-Kombinationen werden durch sogenannte Sektionen unterteilt. Durch den einfachen Aufbau wurde ein Ziel der Ini-Datei erreicht: Sie sollten von Menschen einfach zu lesen sein und betriebssystemübergreifend verwendet werden können.</p>



<h4 class="wp-block-heading" id="ini-datei-beispiel">Ini-Datei Beispiel</h4>



<p>Dem o. g. Wikipedia-Artikel können wir folgendes Beispiel zum Aufbau einer Ini-Datei entnehmen:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="ini" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[Sektion1]
Schlüssel=Wert
[Sektion2]
Schlüssel=Wert
Schlüssel2=Wert</pre>



<p>Dort sehen wir einmal den Abschnitt, bzw. die Sektion Nummer 1 mit einem Schlüssel-Wert-Paar. Und die zweite Sektion mit letztendlich 2 Schlüssel-Wert-Paaren.</p>



<h2 class="wp-block-heading" id="net-api-funktionen-deklarieren">WinAPI-Funktionen für INI-Zugriff deklarieren</h2>



<p>Damit wir von einer Ini Datei lesen, bzw. in eine Ini Datei schreiben können, müssen wir zuerst einmal die passenden .NET API-Funktionen deklarieren.</p>



<h3 class="wp-block-heading" id="byval-haufig-in-online-beispielen">ByVal – Häufig in Online-Beispielen</h3>



<p>Vermutlich wirst Du in Codes die du online findest häufig das &#8222;ByVal&#8220;-Schlüsselwort sehen. Was das Schlüsselwort bewirkt, bzw. wofür es ist, findet sicher an einer anderen Stelle seinen Platz, hier würde es den Rahmen sprengen bzw. thematisch nicht passen. Für unsere Deklarationen ist es nicht notwendig, da &#8222;primitive&#8220; Parameter – wenn nicht anders angegeben – sowieso standardmäßig &#8222;By Value&#8220; übergeben werden.</p>



<h3 class="wp-block-heading" id="getprivateprofilestring-api-funktion">GetPrivateProfileString API-Funktion</h3>



<p>Als erstes wäre da die &#8222;<strong>GetPrivateProfileString</strong>&#8222;-Funktion, Welche dafür verantwortlich ist, Informationen aus einer INI-Datei zu lesen.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;DllImport("kernel32.dll", SetLastError:=True)>
Private Shared Function GetPrivateProfileString(
                        lpAppName As String,
                        lpKeyName As String,
                        lpDefault As String,
                        lpReturnedString As StringBuilder,
                        nSize As Integer,
                        lpFileName As String) As Integer
End Function</pre>



<h3 class="wp-block-heading" id="writeprivateprofilestring-api-funktion">WritePrivateProfileString API-Funktion</h3>



<p>Dann wäre die &#8222;<strong>WritePrivateProfileString</strong>&#8222;-Funktion an der Reihe, Welche dafür verantwortlich ist, Informationen in eine INI-Datei zu schreiben.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">&lt;DllImport("kernel32.dll", SetLastError:=True)>
Private Shared Function WritePrivateProfileString(
                    lpAppName As String,
                    lpKeyName As String,
                    lpString As String,
                    lpFileName As String) As Boolean
End Function</pre>



<h2 class="wp-block-heading" id="der-erste-code-im-einsatz-rohe-api">INI-Datei mit WinAPI lesen und schreiben</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Ini-Datei-lesen-und-schreiben-mit-rohen-Win-API-Funktionen.jpg"><img decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/Ini-Datei-lesen-und-schreiben-mit-rohen-Win-API-Funktionen.jpg" alt="Ini Datei lesen und schreiben mit rohen Win-API Funktionen" class="wp-image-9354" title="Ini Datei lesen und schreiben mit rohen Win-API Funktionen"/></a><figcaption class="wp-element-caption">Ini Datei lesen und schreiben mit rohen Win-API Funktionen</figcaption></figure>



<p>Mit diesen beiden Windows API-Funktionen können wir eigentlich schon alles Notwendige erledigen. Wir können Einstellungen aus einer Sektion Laden und Einstellungen in eine Sektion speichern. Schön sieht das Ganze bisher aber noch nicht so ganz aus, daher werde ich das später noch in eine andere Hilfsfunktion wrappen.</p>



<h3 class="wp-block-heading" id="einstellung-speichern-schreiben">Einstellung speichern/schreiben</h3>



<p>Mit diesem kleinen Code-Snippet schreiben wir im aktuellen Verzeichnis der Anwendung eine &#8222;config.ini&#8220; Datei. Dort wird unter der Sektion namens &#8222;Sektion1&#8220; ein Schlüssel namens &#8222;Schlüssel&#8220; angelegt, bzw. überschrieben. In diesem Schlüssel speichern wir den Wert &#8222;Wert&#8220; ab.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim wroteSuccessfully = WritePrivateProfileString("Sektion1", "Schlüssel", "Wert", ".\config.ini")</pre>



<h3 class="wp-block-heading" id="einstellung-laden-lesen">Einstellung laden/lesen</h3>



<p>Etwas komplizierter sieht es dann beim Lese-Beispiel aus:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim bufferLength = 1024
Dim stringBuilder = New StringBuilder(bufferLength)
Dim length = GetPrivateProfileString("Sektion1", "Schlüssel", "Standardwert", stringBuilder, bufferLength, ".\config.ini")
Dim readValue = stringBuilder.ToString().Substring(0, length)
' do something with readValue</pre>



<h2 class="wp-block-heading" id="vereinfachung-durch-klasse-ini-datei-lesen-und-schreiben">INI-Zugriff per Wrapper-Klasse vereinfachen</h2>



<p>In den nächsten Zeilen habe ich mal den ersten Versuch einer kleinen Ini-Klasse dargestellt. Diese soll den Zugriff auf die Ini-Funktionalitäten kapseln, vereinfachen und natürlich auch verständlicher machen. Dadurch können wir dann ganz einfach eine Ini Datei lesen und schreiben. Zu einem späteren Zeitpunkt werde ich in diesem Beitrag ggf. auch eine komplett objektorientierte Variante erstellen.</p>



<h3 class="wp-block-heading" id="die-erste-ini-klasse">Die erste Ini-Klasse</h3>



<p>Hier siehst Du die erste kleine Ini-Klasse, dadurch werden wir den Zugriff auf die API-Funktionen vereinfachen. Ich habe die &#8222;rohen&#8220; API-Funktionen beabsichtigt als &#8222;Public&#8220; drin gelassen, um auch einen direkten Zugriff zu ermöglichen. Auch wenn die Klasse zuerst rudimentär zu sehen ist, hilft Sie uns dennoch eine Ini Datei lesen und schreiben zu können.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.Runtime.InteropServices
Imports System.Text

Public Class Ini

    &lt;DllImport("kernel32.dll", SetLastError:=True)>
    Public Shared Function GetPrivateProfileString(lpAppName As String,
                        lpKeyName As String,
                        lpDefault As String,
                        lpReturnedString As StringBuilder,
                        nSize As Integer,
                        lpFileName As String) As Integer
    End Function

    &lt;DllImport("kernel32.dll", SetLastError:=True)>
    Public Shared Function WritePrivateProfileString(lpAppName As String,
                        lpKeyName As String,
                        lpString As String,
                        lpFileName As String) As Boolean
    End Function


    Public Property Sections As List(Of Section)

    ''' &lt;summary>
    ''' Helper to retrieve the section of the given index
    ''' &lt;/summary>
    ''' &lt;exception cref="IndexOutOfRangeException">&lt;/exception>
    Default Public ReadOnly Property Section(index As Integer) As Section
        Get
            Dim foundSection As Section = Sections.ElementAtOrDefault(index)
            If foundSection Is Nothing Then
                Throw New IndexOutOfRangeException($"No section at the given index of {index} found")
            End If
            Return foundSection
        End Get
    End Property

    ''' &lt;summary>
    ''' Helper to retrieve the entry of the given key
    ''' &lt;/summary>
    ''' &lt;exception cref="IndexOutOfRangeException">&lt;/exception>
    Default Public ReadOnly Property Section(name As String) As Section
        Get
            Dim foundSection As Section = Sections.SingleOrDefault(Function(x) x.Name.ToLower() = name)
            If foundSection Is Nothing Then
                Throw New IndexOutOfRangeException($"No section for the name {name} found")
            End If
            Return foundSection
        End Get
    End Property

    Sub New()
        Sections = New List(Of Section)
    End Sub

    ''' &lt;summary>
    ''' Helper function to read single values if needed
    ''' &lt;/summary>
    Public Shared Function ReadValue(section As String, key As String, [default] As String, file As String) As String
        Dim bufferLength = 1024
        Dim stringBuilder = New StringBuilder(bufferLength)
        Dim length = GetPrivateProfileString(section, key, [default], stringBuilder, bufferLength, file)
        Dim bufferedValue = stringBuilder.ToString()
        Dim value = bufferedValue.Substring(0, length)
        Return value
    End Function

    ''' &lt;summary>
    ''' Helper function to write single values if needed
    ''' &lt;/summary>
    Public Shared Function WriteValue(section As String, key As String, value As String, file As String) As Boolean
        Dim wroteSuccessfully = WritePrivateProfileString(section, key, value, file)
        Return wroteSuccessfully
    End Function

    Public Shared Function Load(contents As String) As Ini
        Dim lines = LoadIniLinesIgnoringEmpty(contents)
        Dim ini = New Ini()
        Dim lastSection As Section = Nothing
        For Each line In lines
            Dim isSection = line.StartsWith("[")
            If isSection Then
                Dim sectionName = Section.GetNameFromLine(line)
                ini.AddSection(sectionName)
                lastSection = ini.Sections.Last()
            Else
                Dim entry = IniFileExample.Entry.FromLine(line)
                lastSection.AddEntry(entry)
            End If
        Next
        Return ini
    End Function

    ''' &lt;summary>
    ''' Adds a section by name
    ''' &lt;/summary>
    ''' &lt;param name="name">The name for the section to add&lt;/param>
    ''' &lt;exception cref="SectionAlreadyExistsException">&lt;/exception>
    Public Sub AddSection(name As String)
        ThrowOnDuplicateSectionName(name)
        Dim section = New Section(name)
        Sections.Add(section)
    End Sub

    ''' &lt;summary>
    ''' Adds a section by instance
    ''' &lt;/summary>
    ''' &lt;param name="section">The section to add&lt;/param>
    ''' &lt;exception cref="SectionAlreadyExistsException">&lt;/exception>
    Public Sub AddSection(section As Section)
        ThrowOnDuplicateSectionName(section.Name)
        Sections.Add(section)
    End Sub

    ''' &lt;summary>
    ''' Helper function to avoid duplicated code
    ''' &lt;/summary>
    ''' &lt;param name="name">The &lt;see cref="Section"/>-Name to check for&lt;/param>
    ''' &lt;exception cref="SectionAlreadyExistsException">&lt;/exception>
    Private Sub ThrowOnDuplicateSectionName(name As String)
        Dim existentSection = Sections.SingleOrDefault(Function(x) x.Name.ToLower() = name.ToLower())
        If existentSection IsNot Nothing Then
            Throw New SectionAlreadyExistsException("A section with the name " &amp; name &amp; " already exists")
        End If
    End Sub

    Private Shared Function LoadIniLinesIgnoringEmpty(contents As String) As List(Of String)
        Dim lines = contents.Split(New String() {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
        Return lines.ToList()
    End Function

    ''' &lt;summary>
    ''' Loads the ini instance from the given filepath
    ''' &lt;/summary>
    ''' &lt;param name="filepath">The filepath to the ini file&lt;/param>
    ''' &lt;param name="encoding">The encoding to use - defaults to UTF8&lt;/param>
    ''' &lt;returns>The parsed ini file instance&lt;/returns>
    Public Shared Async Function Load(filepath As String, Optional encoding As Encoding = Nothing) As Task(Of Ini)
        If encoding Is Nothing Then
            encoding = Encoding.UTF8
        End If
        Dim contents = Await IO.File.ReadAllTextAsync(filepath, encoding)
        Dim ini = Load(contents)
        Return ini
    End Function

    ''' &lt;summary>
    ''' Saves the ini instance to the given location
    ''' &lt;/summary>
    ''' &lt;param name="filepath">The filepath to save the ini file to&lt;/param>
    ''' &lt;param name="encoding">The encoding to use - defaults to UTF8&lt;/param>
    Public Async Function Save(filepath As String, Optional encoding As Encoding = Nothing) As Task
        If encoding Is Nothing Then
            encoding = Encoding.UTF8
        End If
        Dim iniString = ToIniString()
        Await IO.File.WriteAllTextAsync(filepath, iniString, encoding)
    End Function

    ''' &lt;summary>
    ''' Creates a string representing the ini file
    ''' &lt;/summary>
    Public Function ToIniString() As String
        Dim sectionStrings = New List(Of String)
        For Each section In Sections
            sectionStrings.Add(section.ToIniString())
        Next
        Return String.Join(Environment.NewLine, sectionStrings)
    End Function

    Public Overrides Function ToString() As String
        Return $"Ini Sections: {Sections.Count}, Entries: {Sections.SelectMany(Function(x) x.Entries).Count()}"
    End Function

End Class</pre>



<h3 class="wp-block-heading" id="wert-lesen">Wert aus Ini-Datei lesen</h3>



<p>Die Funktion &#8222;ReadValue&#8220; vereinfacht den Zugriff auf die &#8222;GetPrivateProfileString&#8220; API-Funktion. Du musst dich nun nicht länger um den Buffer, oder den StringBuilder kümmern. &#8222;ReadValue&#8220;ist bewusst als Shared gekennzeichnet, damit keine Instanz der Ini-Klasse von Nöten ist.</p>



<p>Ist wie gesagt nur ein Beispiel &#8211; der Shared Modifier nimmt uns prinzipiell die Möglichkeit Unit-Tests durchzuführen. Aber auch das ist ein Thema für ein anderes Mal.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim value = Ini.ReadValue("Sektion1", "Schlüssel", "Standardwert", ".\config.ini")</pre>



<h3 class="wp-block-heading" id="wert-schreiben">Wert in Ini-Datei schreiben</h3>



<p>Die Funktion &#8222;WriteValue&#8220; vereinfacht uns den Zugriff auf die dahinter befindliche &#8222;WritePrivateProfileString&#8220; API-Funktion nur mäßig, aber um einheitlich zu bleiben, lassen wir Sie drin.</p>



<p>So schreiben wir mit der neuen Klasse einen Wert in unsere Ini-Datei:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim wroteSuccessfully = Ini.WriteValue("Sektion1", "Schlüssel", "Wert", ".\config.ini")</pre>



<h2 class="wp-block-heading" id="vollstandiger-code-1-wrapper-methoden">Vollständiger Code 1 – Wrapper Methoden</h2>



<p>Anbei ist der vollständige Code mit kleinen Wrappern um die nativen API-Funktionen herum. Diesen kannst Du einfach <strong>in </strong>eine separate Datei innerhalb deines Projekts anlegen. Benenne die Datei z. B. als &#8222;Ini.vb&#8220; und verwende je nach Bedarf &amp; Stil ggf. noch einen sauberen Namespace wie &#8222;Utils&#8220;.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.Runtime.InteropServices
Imports System.Text

Public Class Ini

    Public Const INI_WRITE_ERROR = 0
    Public Const INI_WRITE_SUCCESS = 1

    Public Shared Function ReadValue(section As String, key As String, [default] As String, file As String) As String
        Dim bufferLength = 1024
        Dim stringBuilder = New StringBuilder(bufferLength)
        Dim length = GetPrivateProfileString(section, key, [default], stringBuilder, bufferLength, file)
        Dim bufferedValue = stringBuilder.ToString()
        Dim value = bufferedValue.Substring(0, length)
        Return value
    End Function

    Public Shared Function WriteValue(section As String, key As String, value As String, file As String) As Boolean
        Dim resultCode = WritePrivateProfileString(section, key, value, file)
        Dim successfulWrite = resultCode = INI_WRITE_SUCCESS
        Return successfulWrite
    End Function

    &lt;DllImport("kernel32.dll", SetLastError:=True)>
    Public Shared Function GetPrivateProfileString(lpAppName As String,
                        lpKeyName As String,
                        lpDefault As String,
                        lpReturnedString As StringBuilder,
                        nSize As Integer,
                        lpFileName As String) As Integer
    End Function

    &lt;DllImport("kernel32.dll", SetLastError:=True)>
    Public Shared Function WritePrivateProfileString(lpAppName As String,
                        lpKeyName As String,
                        lpString As String,
                        lpFileName As String) As Boolean
    End Function

End Class</pre>



<h2 class="wp-block-heading" id="vollstandiger-code-2-starke-objektorientierung">Vollständiger Code 2 – Objektorientierte INI-Klasse (Entry, Section, Ini)</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Ini-Datei-lesen-und-schreiben-mit-Objektorientierung.jpg"><img decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/Ini-Datei-lesen-und-schreiben-mit-Objektorientierung.jpg" alt="Ini Datei lesen und schreiben mit Objektorientierung" class="wp-image-9352" title="Ini Datei lesen und schreiben mit Objektorientierung"/></a><figcaption class="wp-element-caption">Ini Datei lesen und schreiben mit Objektorientierung</figcaption></figure>



<p>Im zweiten Beispiel des vollständigen Codes habe ich versucht, die Ini-Datei so objektorientiert wie möglich darzustellen. Sicherlich könnte man hier und da ggf. noch etwas vereinfachen, oder optimieren, ich denke allerdings, dass das ein guter Anfang ist. Hierfür habe ich mehrere Dateien angelegt, die ja jeder so namespacen etc. kann, wie er es für richtig hält.</p>



<p>Ebenso habe ich alle Funktionen beschrieben, was die Benutzung dementsprechend einfach gestalten sollte.</p>



<h3 class="wp-block-heading" id="die-entry-klasse">Die Entry-Klasse</h3>



<p>Jede Instanz der Entry-Klasse stellt praktisch eine Zeile aus einer Ini-Datei, bzw. genauer genommen einer Sektion dar.</p>



<h4 class="wp-block-heading" id="eigenschaften-properties">Eigenschaften / Properties</h4>



<h5 class="wp-block-heading" id="key">Key</h5>



<p>Repräsentiert den Schlüssel des Eintrags</p>



<h5 class="wp-block-heading" id="value">Value</h5>



<p>Steht für den Wert des Eintrags</p>



<h4 class="wp-block-heading" id="konstruktor">Konstruktor</h4>



<h5 class="wp-block-heading" id="new-key">New(key)</h5>



<p>Um einen Eintrag nur mit einem Schlüssel und leerem String als Wert zu erstellen.</p>



<h5 class="wp-block-heading" id="new-key-value">New(key, value)</h5>



<p>Erstellt einen Eintrag mit Schlüssel und zugehörigem Wert.</p>



<h4 class="wp-block-heading" id="methoden">Methoden</h4>



<h5 class="wp-block-heading" id="fromline">FromLine</h5>



<p>Die &#8222;Shared&#8220; FromLine-Funktion soll es uns vereinfachen einen Eintrag aus einer Ini-Datei-Zeile zu verarbeiten. Dabei sucht die Funktion das erste Vorkommen eines Gleichzeichens und verarbeitet den Rest als Wert.</p>



<h5 class="wp-block-heading" id="toinistring">ToIniString</h5>



<p>Eine &#8222;ToIniString&#8220;-Funktion soll es vereinfachen, den Eintrag wieder in eine Ini-Zeile umzuwandeln. Achtung: Ohne Zeilenumbruch!</p>



<h5 class="wp-block-heading" id="tostring">ToString</h5>



<p>Letztlich soll die &#8222;ToString()&#8220;-Funktion für ein anschaulicheres Ergebnis durch standardmäßiges Umwandeln in einen String sorgen.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class Entry

    Public Property Key As String

    Public Property Value As String

    Sub New(key As String)
        Me.Key = key
        Me.Value = ""
    End Sub

    Sub New(key As String, value As String)
        Me.Key = key
        Me.Value = value
    End Sub

    ''' &lt;summary>
    ''' Parses an &lt;see cref="Entry"/> from the given ini file line
    ''' &lt;/summary>
    ''' &lt;param name="line">&lt;/param>
    ''' &lt;returns>The parsed Entry&lt;/returns>
    ''' &lt;exception cref="ArgumentException">If no key value separator could be found&lt;/exception>
    Public Shared Function FromLine(line As String) As Entry
        Dim firstIndexOfEqualSign = line.IndexOf("=")
        If firstIndexOfEqualSign = -1 Then
            Throw New ArgumentException("The equal separation sign could not be found", NameOf(line))
        End If
        Dim parts = line.Split("=")
        Dim key = parts(0)
        Dim value = parts(1)
        Dim entry = New Entry(key, value)
        Return entry
    End Function

    ''' &lt;summary>
    ''' Returns the line like it would be represented in an ini file
    ''' No Appended newline!
    ''' &lt;/summary>
    Public Function ToIniString() As String
        Return $"{Key}={Value}"
    End Function

    Public Overrides Function ToString() As String
        Return $"Ini-Entry Key: {Key}, Value: {Value}"
    End Function

End Class</pre>



<h3 class="wp-block-heading" id="die-section-klasse">Die Section-Klasse</h3>



<p>Wie der Name schon verrät, spiegelt diese Klasse eine jeweilige Sektion aus der Ini-Datei wieder.</p>



<h4 class="wp-block-heading" id="eigenschaften-properties">Eigenschaften / Properties</h4>



<h5 class="wp-block-heading" id="name">Name</h5>



<p>Jede Sektion muss einen Namen besitzen, weil die Regeln für Ini-Dateien das so vorschreiben.</p>



<h5 class="wp-block-heading" id="entries">Entries</h5>



<p>Jede Sektion kann eine beliebige Anzahl an Einträgen haben.</p>



<h4 class="wp-block-heading" id="indexer">Indexer</h4>



<p>Vor dem Konstruktor der Klasse habe ich zwei Indexer für die Sektionen geschrieben, da es so auch aus .NET Standardklassen kennt – und das mag ich!</p>



<p>Mit dem einen können wir über den tatsächlichen Index und mit dem anderen über den Schlüssel an den jeweiligen Eintrag kommen.</p>



<h4 class="wp-block-heading" id="konstruktor">Konstruktor</h4>



<p>Der Konstruktor braucht zumindest einen Namen für die jeweilige Sektion und die Liste der Einträge wird automatisch instanziiert.</p>



<h4 class="wp-block-heading" id="methoden">Methoden</h4>



<h5 class="wp-block-heading" id="getnamefromline">GetNameFromLine</h5>



<p>Diese Funktion soll den Namen einer Sektion aus einer Ini-Zeile extrahieren können. Da Diese ja mit &#8222;[Sektionsname]&#8220; angegeben werden, entfernt die Funktion für uns die Eckigen Klammern und eventuelle Leerzeichen.</p>



<h5 class="wp-block-heading" id="getininameline">GetIniNameLine</h5>



<p>Macht praktisch genau das Gegenteil der vorherigen Funktion: Sie wandelt den Namen wieder in einen für eine Ini brauchbaren Namen a la &#8222;[Sektionsname]&#8220; um.</p>



<h5 class="wp-block-heading" id="addentry-key-value">AddEntry(key, value)</h5>



<p>Fügt der Sektion einen Eintrag anhand des übergebenen Schlüssels und Wertes hinzu. Falls der Schlüssel bereits existiert, wird der Wert darin überschrieben.</p>



<h5 class="wp-block-heading" id="addentry-entry">AddEntry(entry)</h5>



<p>Wie die Methode oben drüber, nur mit einem übergebenen Objekt sozusagen als &#8222;Container&#8220;.</p>



<h5 class="wp-block-heading" id="toinistring">ToIniString</h5>



<p>Gibt die Sektion als String wieder, so wie Sie in der Ini-Datei aussehen würde. Achtung! Kein hinten anstehender Zeilenumbruch!</p>



<h5 class="wp-block-heading" id="tostring">ToString</h5>



<p>Stellt die Informationen über die Sektion als lesbaren String dar, also wie im .NET Framework üblich.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Public Class Section

    Public Property Name As String

    Public Property Entries As List(Of Entry)

    ''' &lt;summary>
    ''' Helper to retrieve the entry of the given index
    ''' &lt;/summary>
    ''' &lt;exception cref="IndexOutOfRangeException">&lt;/exception>
    Default Public ReadOnly Property GetEntry(index As Integer) As Entry
        Get
            Dim foundEntry As Entry = Entries.ElementAtOrDefault(index)
            If foundEntry Is Nothing Then
                Throw New IndexOutOfRangeException($"No entry at the given index of {index} found")
            End If
            Return foundEntry
        End Get
    End Property

    ''' &lt;summary>
    ''' Helper to retrieve the entry of the given key
    ''' &lt;/summary>
    ''' &lt;exception cref="IndexOutOfRangeException">&lt;/exception>
    Default Public ReadOnly Property GetEntry(key As String) As Entry
        Get
            Dim foundEntry As Entry = Entries.SingleOrDefault(Function(x) x.Key.ToLower() = key.ToLower())
            If foundEntry Is Nothing Then
                Throw New IndexOutOfRangeException($"No entry for the key {key} found")
            End If
            Return foundEntry
        End Get
    End Property

    Sub New(name As String)
        Entries = New List(Of Entry)
        Me.Name = name
    End Sub

    ''' &lt;summary>
    ''' Retrieves the section name from a given ini file line
    ''' &lt;/summary>
    ''' &lt;param name="line">The ini line to retrieve from&lt;/param>
    Public Shared Function GetNameFromLine(line As String) As String
        Return line.Replace("[", "").Replace("]", "").Replace(" ", "")
    End Function

    ''' &lt;summary>
    ''' Retrieves the section name like it would be represented in an ini file
    ''' No appended newline!
    ''' &lt;/summary>
    Public Function GetIniNameLine() As String
        Return $"[{Name}]"
    End Function

    ' TODO - Maybe validate given key and value..
    ''' &lt;summary>
    ''' Adds an &lt;see cref="Entry"/> by key and value
    ''' If the key already exists, the value will be overriden
    ''' &lt;/summary>
    ''' &lt;param name="entry">The key of the entry&lt;/param>
    ''' &lt;param name="value">The value of the entry&lt;/param>
    ''' &lt;returns>The &lt;see cref="Section"/> to chain multiple calls&lt;/returns>
    Public Function AddEntry(key As String, value As String) As Section
        Dim existentEntry = Entries.SingleOrDefault(Function(x) x.Key.ToLower() = key.ToLower())
        If existentEntry IsNot Nothing Then
            existentEntry.Value = value
            Return Me
        End If
        Dim entry = New Entry(key, value)
        Entries.Add(entry)
        Return Me
    End Function

    ' TODO - Maybe validate given entries key and value..
    ''' &lt;summary>
    ''' Adds a given &lt;see cref="Entry"/>
    ''' If the key already exists, the value will be overriden
    ''' &lt;/summary>
    ''' &lt;param name="entry">The Entry to add&lt;/param>
    ''' &lt;returns>The &lt;see cref="Section"/> to chain multiple calls&lt;/returns>
    Public Function AddEntry(entry As Entry) As Section
        Dim existentEntry = Entries.SingleOrDefault(Function(x) x.Key.ToLower() = entry.Key.ToLower())
        If existentEntry IsNot Nothing Then
            existentEntry.Value = entry.Value
            Return Me
        End If
        Entries.Add(entry)
        Return Me
    End Function

    ''' &lt;summary>
    ''' Returns the string representing the section inside an ini file
    ''' Without appended newline!
    ''' &lt;/summary>
    Public Function ToIniString() As String
        Dim lines = New List(Of String)
        lines.Add(GetIniNameLine())
        For Each entry In Entries
            lines.Add(entry.ToIniString())
        Next
        Dim iniString = String.Join(Environment.NewLine, lines)
        Return iniString
    End Function

    Public Overrides Function ToString() As String
        Return $"Section {Name}, Entries: {Entries.Count}"
    End Function

End Class</pre>



<h3 class="wp-block-heading" id="die-ini-klasse">Die Ini-Klasse</h3>



<p>Repräsentiert die Initialisierungsdatei mit all Ihren Eigenschaften und Methoden selbst, daher hilft sie noch einmal durch starke Objektorientierung, eine Ini Datei lesen und schreiben zu können.</p>



<h4 class="wp-block-heading" id="eigenschaften-properties">Eigenschaften / Properties</h4>



<h5 class="wp-block-heading" id="sections">Sections</h5>



<p>Repräsentiert die einzelnen Sektionen der Ini-Datei.</p>



<h4 class="wp-block-heading" id="indexer">Indexer</h4>



<p>Vor dem Konstruktor der Klasse habe ich zwei Indexer für die Ini geschrieben, so kennt man es auch aus .NET Standardklassen – und das mag ich!</p>



<p>Mit dem einen können wir über den tatsächlichen Index und mit dem anderen über den Namen an die jeweilige Sektion kommen.</p>



<h4 class="wp-block-heading" id="konstruktor">Konstruktor</h4>



<p>Der Konstruktor braucht bisher keinen Parameter und die Liste der Sektionen wird automatisch instanziiert.</p>



<h4 class="wp-block-heading" id="methoden">Methoden</h4>



<h5 class="wp-block-heading" id="readvalue">ReadValue</h5>



<p>Die Funktion haben wir <a href="#wert-lesen">weiter oben</a> schon besprochen, daher entfällt die Erklärung hier.</p>



<h5 class="wp-block-heading" id="writevalue">WriteValue</h5>



<p>Haben wir ebenfalls schon <a href="#wert-schreiben">weiter oben</a> besprochen, insofern entfällt die Erklärung hier.</p>



<h5 class="wp-block-heading" id="load-contents">Load(contents)</h5>



<p>Lädt eine Ini-Instanz anhand ihres String-Inhalts, indem es die Teile mit Hilfsfunktionen verarbeitet.</p>



<h5 class="wp-block-heading" id="load-filepath-encoding">Load(filepath, encoding)</h5>



<p>Lädt die Inhalte einer Textdatei mit dem angegebenen Encoding (sonst UTF8) und wandelt Sie mit der anderen Überladung in eine Ini-Datei um.</p>



<h5 class="wp-block-heading" id="addsection-name">AddSection(name)</h5>



<p>Fügt eine Sektion anhand eines Namens hinzu. Wirft eine SectionAlreadyExistsException, falls die übergebene Sektion bereits existiert.</p>



<h5 class="wp-block-heading" id="addsection-section">AddSection(section)</h5>



<p>Macht das Gleiche wie die Funktion hier drüber, jedoch mit einer Instanz.</p>



<h5 class="wp-block-heading" id="throwonduplicatesectionname">ThrowOnDuplicateSectionName</h5>



<p>Hilfs-Methode um &#8222;Duplicate-Content&#8220; zu vermeiden. Checkt ob eine Sektion mit dem gegebenen Namen schon existiert und schmeißt eine SectionAlreadyExistsException wenn ja.</p>



<h5 class="wp-block-heading" id="loadinilinesignoringempty">LoadIniLinesIgnoringEmpty</h5>



<p>Lädt die Zeilen einer Ini-Datei in eine String-Liste und überspringt leere Zeilen.</p>



<h5 class="wp-block-heading" id="save">Save</h5>



<p>Speichert die Ini als Datei unter dem angegebenen Dateinamen und mit dem Encoding (sonst UTF8) im System ab.</p>



<h5 class="wp-block-heading" id="toinistring">ToIniString</h5>



<p>Wandelt die Ini-Instanz in einen speicherbaren String um.</p>



<p>ToString</p>



<p>Zeigt Informationen über die Ini in lesbarer Form an.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.Runtime.InteropServices
Imports System.Text

Public Class Ini

    &lt;DllImport("kernel32.dll", SetLastError:=True)>
    Public Shared Function GetPrivateProfileString(lpAppName As String,
                        lpKeyName As String,
                        lpDefault As String,
                        lpReturnedString As StringBuilder,
                        nSize As Integer,
                        lpFileName As String) As Integer
    End Function

    &lt;DllImport("kernel32.dll", SetLastError:=True)>
    Public Shared Function WritePrivateProfileString(lpAppName As String,
                        lpKeyName As String,
                        lpString As String,
                        lpFileName As String) As Boolean
    End Function


    Public Property Sections As List(Of Section)

    ''' &lt;summary>
    ''' Helper to retrieve the section of the given index
    ''' &lt;/summary>
    ''' &lt;exception cref="IndexOutOfRangeException">&lt;/exception>
    Default Public ReadOnly Property GetSection(index As Integer) As Section
        Get
            Dim foundSection As Section = Sections.ElementAtOrDefault(index)
            If foundSection Is Nothing Then
                Throw New IndexOutOfRangeException($"No section at the given index of {index} found")
            End If
            Return foundSection
        End Get
    End Property

    ''' &lt;summary>
    ''' Helper to retrieve the entry of the given name
    ''' &lt;/summary>
    ''' &lt;exception cref="IndexOutOfRangeException">&lt;/exception>
    Default Public ReadOnly Property GetSection(name As String) As Section
        Get
            Dim foundSection As Section = Sections.SingleOrDefault(Function(x) x.Name.ToLower() = name.ToLower())
            If foundSection Is Nothing Then
                Throw New IndexOutOfRangeException($"No section for the name {name} found")
            End If
            Return foundSection
        End Get
    End Property

    Sub New()
        Sections = New List(Of Section)
    End Sub

    ''' &lt;summary>
    ''' Helper function to read single values if needed
    ''' &lt;/summary>
    Public Shared Function ReadValue(section As String, key As String, [default] As String, file As String) As String
        Dim bufferLength = 1024
        Dim stringBuilder = New StringBuilder(bufferLength)
        Dim length = GetPrivateProfileString(section, key, [default], stringBuilder, bufferLength, file)
        Dim bufferedValue = stringBuilder.ToString()
        Dim value = bufferedValue.Substring(0, length)
        Return value
    End Function

    ''' &lt;summary>
    ''' Helper function to write single values if needed
    ''' &lt;/summary>
    Public Shared Function WriteValue(section As String, key As String, value As String, file As String) As Boolean
        Dim wroteSuccessfully = WritePrivateProfileString(section, key, value, file)
        Return wroteSuccessfully
    End Function

    ''' &lt;summary>
    ''' Adds a section by name
    ''' &lt;/summary>
    ''' &lt;param name="name">The name for the section to add&lt;/param>
    ''' &lt;exception cref="SectionAlreadyExistsException">&lt;/exception>
    Public Sub AddSection(name As String)
        ThrowOnDuplicateSectionName(name)
        Dim section = New Section(name)
        Sections.Add(section)
    End Sub

    ''' &lt;summary>
    ''' Adds a section by instance
    ''' &lt;/summary>
    ''' &lt;param name="section">The section to add&lt;/param>
    ''' &lt;exception cref="SectionAlreadyExistsException">&lt;/exception>
    Public Sub AddSection(section As Section)
        ThrowOnDuplicateSectionName(section.Name)
        Sections.Add(section)
    End Sub

    ''' &lt;summary>
    ''' Helper function to avoid duplicated code
    ''' &lt;/summary>
    ''' &lt;param name="name">The &lt;see cref="Section"/>-Name to check for&lt;/param>
    ''' &lt;exception cref="SectionAlreadyExistsException">&lt;/exception>
    Private Sub ThrowOnDuplicateSectionName(name As String)
        Dim existentSection = Sections.SingleOrDefault(Function(x) x.Name.ToLower() = name.ToLower())
        If existentSection IsNot Nothing Then
            Throw New SectionAlreadyExistsException("A section with the name " &amp; name &amp; " already exists")
        End If
    End Sub

    Private Shared Function LoadIniLinesIgnoringEmpty(contents As String) As List(Of String)
        Dim lines = contents.Split(New String() {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
        Return lines.ToList()
    End Function

    Public Shared Function Load(contents As String) As Ini
        Dim lines = LoadIniLinesIgnoringEmpty(contents)
        Dim ini = New Ini()
        Dim lastSection As Section = Nothing
        For Each line In lines
            Dim isSection = line.StartsWith("[")
            If isSection Then
                Dim sectionName = Section.GetNameFromLine(line)
                ini.AddSection(sectionName)
                lastSection = ini.Sections.Last()
            Else
                Dim theEntry= IniFileExample.Entry.FromLine(line)
                lastSection.AddEntry(theEntry)
            End If
        Next
        Return ini
    End Function

    ''' &lt;summary>
    ''' Loads the ini instance from the given filepath
    ''' &lt;/summary>
    ''' &lt;param name="filepath">The filepath to the ini file&lt;/param>
    ''' &lt;param name="encoding">The encoding to use - defaults to UTF8&lt;/param>
    ''' &lt;returns>The parsed ini file instance&lt;/returns>
    Public Shared Async Function Load(filepath As String, Optional encoding As Encoding = Nothing) As Task(Of Ini)
        If encoding Is Nothing Then
            encoding = Encoding.UTF8
        End If
        Dim contents = Await IO.File.ReadAllTextAsync(filepath, encoding)
        Dim ini = Load(contents)
        Return ini
    End Function

    ''' &lt;summary>
    ''' Saves the ini instance to the given location
    ''' &lt;/summary>
    ''' &lt;param name="filepath">The filepath to save the ini file to&lt;/param>
    ''' &lt;param name="encoding">The encoding to use - defaults to UTF8&lt;/param>
    Public Async Function Save(filepath As String, Optional encoding As Encoding = Nothing) As Task
        If encoding Is Nothing Then
            encoding = Encoding.UTF8
        End If
        Dim iniString = ToIniString()
        Await IO.File.WriteAllTextAsync(filepath, iniString, encoding)
    End Function

    ''' &lt;summary>
    ''' Creates a string representing the ini file
    ''' &lt;/summary>
    Public Function ToIniString() As String
        Dim sectionStrings = New List(Of String)
        For Each section In Sections
            sectionStrings.Add(section.ToIniString())
        Next
        Return String.Join(Environment.NewLine, sectionStrings)
    End Function

    Public Overrides Function ToString() As String
        Return $"Ini Sections: {Sections.Count}, Entries: {Sections.SelectMany(Function(x) x.Entries).Count()}"
    End Function

End Class</pre>



<h3 class="wp-block-heading" id="eine-vorhandene-ini-datei-lesen">Eine vorhandene Ini-Datei lesen</h3>



<p>Hier ein kleines Beispiel, um mit unserem vollständigen Satz an Klassen ob eine vorhandene Ini-Datei zu lesen. Beachte, dass natürlich eine Datei namens &#8222;config.ini&#8220; tatsächlich in deinem Anwendungs-Pfad existieren muss. Ebenfalls muss die Datei die Sektion &#8222;Sektion1&#8220; und einen passenden Schlüssel &#8222;Key&#8220; beinhalten.</p>



<p>Falls Du mein Beispielprojekt heruntergeladen hast, befindet diese Datei sich dort bereits!</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim config = Await Ini.Load(".\config.ini", Encoding.UTF8)
    Dim section = config.GetSection("Sektion1")
    Dim entry = section("Key")
    Dim value = entry.Value
    MessageBox.Show(value)
End Sub</pre>



<p>Der ganze Code kann natürlich weiter optimiert und vereinfacht werden. Im Optimalfall möchte man ja nur eine Zeile Code a la <code>config.GetString(...)</code> aufrufen können.</p>


<p class="rr-alert info" style="background:#e3f7fc;"><img decoding="async" alt="Info notice" src="https://robbelroot.de/wp-content/themes/pinboard-child/imgs/info.png"><span style="color:black;align-self: center;">Du nutzt noch eine ältere .NET-Framework-Version ohne Async/Await? Dann kannst Du das Laden der INI-Datei auch <a href="https://robbelroot.de/blog/backgroundworker-beispiel-in-vb-net/">in einen BackgroundWorker auslagern</a>, um die Benutzeroberfläche nicht zu blockieren.</span></p>



<h3 class="wp-block-heading" id="eine-ini-datei-erstellen">Eine Ini-Datei erstellen</h3>



<p>Hier kommt ein schnelles Beispiel, um eine neue Ini-Datei zu erstellen.</p>



<p>(Achtung, überschreibt die vorhandene Ini-Datei durch die &#8222;Save&#8220;-Methode!).</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim config = New Ini()
    config.AddSection("Sektion2")
    Dim section = config("Sektion2")
    section.AddEntry("Key1", "Value1")
    section.AddEntry("Key2", "Value2")
    Await config.Save(".\config.ini", Encoding.UTF8)
End Sub</pre>



<h3 class="wp-block-heading" id="die-vorhandene-ini-datei-bearbeiten">Die vorhandene Ini-Datei &#8222;bearbeiten&#8220;</h3>



<p>Nun der letzte Schritt, wo wir eine vorhandene Datei bearbeiten, bzw. verändern.</p>



<p>Achte hierbei je nach Vorgehensweise darauf, dass eine Sektion noch nicht existiert.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim config = Await Ini.Load(".\config.ini", Encoding.UTF8)
    Dim section = config.GetSection("Sektion2")
    Dim entry = section("Key2")
    entry.Value = "NewValue!"
    Await config.Save(".\config.ini", Encoding.UTF8)
End Sub</pre>



<h2 class="wp-block-heading" id="fazit">Fazit</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Tutorial-1-Fazit.jpg"><img loading="lazy" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Tutorial-1-Fazit.jpg" alt="Ini Datei lesen und schreiben – Das Fazit" class="wp-image-8236" title="Ini Datei lesen und schreiben – Das Fazit"/></a><figcaption class="wp-element-caption">Ini Datei lesen und schreiben – Das Fazit</figcaption></figure>



<p>Am Ende des Beitrag angekommen, wiederholen wir nochmal kurz die wichtigsten Dinge.</p>



<p>Ini-Dateien sind wohl der gute alte Weg schlechthin, um Einstellungen zu speichern. Trotz ihres langen Bestehens sind Sie auch heute noch ein gerne genutztes Werkzeug. Durch den einfachen Aufbau und den Verzicht auf kryptische Zeichen, sind Sie von Menschen einfach zu lesen.</p>



<p>Das hat viele Nutzer der Ini-Dateien in Vergangenheit das ein oder andere Mal gerettet, wenn es hieß: &#8222;Kannst Du mal eben die Einstellung anpassen?&#8220;.</p>



<p>In diesem Beitrag habe ich Dir gezeigt, wie Du mit Hilfe von Visual Basic NET eigene Ini-Dateien verwalten kannst. Das bezog sich einerseits auf das typische Lesen, aber natürlich auch auf das Setzen von Einstellungen. Dabei gab es von der rohen &#8222;API-Variante&#8220;, bis zu Hilfs-Methoden und der vollständigen Objektorientierung alles.</p>



<p>Meine klare Empfehlung für den Umgang mit INI-Dateien: Setze auf die objektorientierte Variante. Im Vergleich zur rohen WinAPI bietet sie Dir eine <strong>Reihe handfester Vorteile</strong>:</p>



<ul class="wp-block-list">
<li><strong>Einfache Bedienbarkeit</strong>: Statt kryptischer API-Aufrufe mit StringBuilder und Buffer arbeitest Du mit verständlichen Methoden wie <code>Ini.Load()</code>, <code>AddSection()</code> und <code>Save()</code>.</li>



<li><strong>Saubere Zugriffe</strong>: Indexer und typisierte Eigenschaften sorgen dafür, dass Du über <code>config("Sektion1")("Key").Value</code> direkt an Deine Werte kommst.</li>



<li><strong>Erweiterbarkeit</strong>: Neue Funktionen wie Validierung, Verschlüsselung oder Standardwerte lassen sich sauber in die bestehenden Klassen integrieren.</li>



<li><strong>Testbarkeit</strong>: Objektorientierter Code lässt sich per Unit-Test absichern, rohe API-Aufrufe praktisch nicht.</li>



<li><strong>Wiederverwendbarkeit</strong>: Die drei Klassen (Ini, Section, Entry) kannst Du 1:1 in jedes weitere Projekt übernehmen.</li>
</ul>


<p class="rr-alert info" style="background:#e3f7fc;"><img decoding="async" alt="Info notice" src="https://robbelroot.de/wp-content/themes/pinboard-child/imgs/info.png"><span style="color:black;align-self: center;">Übrigens: Wenn Du Einstellungen wie die Sprachauswahl Deiner Anwendung speichern möchtest, lohnt sich ein Blick auf mein <a href="https://robbelroot.de/blog/rskibbe-i18n-translate-create-multilanguage-winforms-wpf-apps/">rskibbe.I18n NuGet-Paket für einfache Mehrsprachigkeit in VB.NET (EN Artikel)</a> — damit kannst Du Deine App im Handumdrehen mehrsprachig machen und die gewählte Sprache bequem in einer INI-Datei persistieren.</span></p>



<h2 class="wp-block-heading">FAQ</h2>



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-question-1774506095276"><strong class="schema-faq-question"><strong>Wie lese ich eine INI-Datei in VB.NET?</strong></strong> <p class="schema-faq-answer">Mit der WinAPI-Funktion <code>GetPrivateProfileString</code> aus kernel32.dll oder objektorientiert über eine eigene Ini-Klasse mit <code>Ini.Load()</code>. Alternativ: das NuGet-Paket rskibbe.Ini verwenden.</p> </div> <div class="schema-faq-section" id="faq-question-1774506102680"><strong class="schema-faq-question"><strong>Was ist eine INI-Datei?</strong></strong> <p class="schema-faq-answer">Eine Konfigurationsdatei mit Sektionen und Schlüssel-Wert-Paaren. Sie wird seit den frühen Windows-Versionen zum Speichern von Programmeinstellungen genutzt und ist auch heute noch verbreitet.</p> </div> <div class="schema-faq-section" id="faq-question-1774506120302"><strong class="schema-faq-question"><strong>Welche Alternative gibt es zu INI-Dateien in .NET?</strong></strong> <p class="schema-faq-answer">JSON (<code>System.Text.Json</code>), XML (<code>App.config</code>) oder <code>My.Settings</code> (nur VB.NET). INI-Dateien bleiben aber beliebt, weil sie ohne zusätzliche Bibliotheken menschenlesbar und einfach editierbar sind.</p> </div> <div class="schema-faq-section" id="faq-question-1774507755047"><strong class="schema-faq-question"><strong>Warum sollte ich INI-Zugriffe objektorientiert umsetzen?</strong></strong> <p class="schema-faq-answer">Objektorientierte INI-Klassen bieten saubere Zugriffe über Indexer und typisierte Eigenschaften, sind leicht erweiterbar und lassen sich per Unit-Test absichern. Im Vergleich zu rohen WinAPI-Aufrufen sparst Du Dir den Umgang mit StringBuilder und Buffern.</p> </div> <div class="schema-faq-section" id="faq-question-1774507760777"><strong class="schema-faq-question"><strong>Kann ich INI-Dateien auch ohne WinAPI verwenden?</strong></strong> <p class="schema-faq-answer">Ja. Mit OOP-Klassen wie der im Beitrag gezeigten Ini-Klasse oder dem NuGet-Paket rskibbe.Ini liest und schreibst Du INI-Dateien rein in .NET, ohne Abhängigkeit zur Windows kernel32.dll. Das funktioniert auch plattformübergreifend unter .NET Core / .NET 5+.</p> </div> </div>



<h2 class="wp-block-heading" id="downloads">Downloads</h2>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/downloads/vbnet/IniFileExample.zip" target="_blank" rel="noreferrer noopener">IniFileExample.zip</a></div>
</div>



<h2 class="wp-block-heading" id="weiterfuhrende-links">Weiterführende Links</h2>



<ul class="wp-block-list">
<li><strong><a href="https://robbelroot.de/blog/ein-vb-net-datagridview-fuellen/" target="_blank" rel="noreferrer noopener">Ein VB NET DataGridView füllen</a></strong></li>



<li><strong><a href="https://robbelroot.de/blog/dein-vb-net-plugin-system-im-eigenbau-das-ultimative-beispiel/" target="_blank" rel="noreferrer noopener">Dein eigenes VB NET Plugin-System</a></strong></li>



<li><strong><a href="https://robbelroot.de/blog/vb-net-webbrowser-alternative/" target="_blank" rel="noreferrer noopener">Eine alternative zum Webbrowser-Control in Winforms/WPF</a></strong></li>
</ul>
<p>Der Beitrag <a href="https://robbelroot.de/blog/ini-datei-lesen-und-schreiben-einstellungen-speichern/">Ini Datei lesen und schreiben in VB.NET – Einstellungen speichern / laden</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/ini-datei-lesen-und-schreiben-einstellungen-speichern/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
	</channel>
</rss>
