# Alle Schlüssel aus Sektion auslesen



## CopWorker (16. April 2021)

Hallo zusammen, 

wollte gerade aus einer *.ini - Datei Sektion alle vorhandenen Schlüssel auslesen. 
Leider funktioniert das nicht. 
Ich bin auch nach Anweisung von Microsoft gefolgt:
GetPrivateProfileString function (winbase.h) - Win32 apps
Da lese ich, dass wenn man den Parameter 2 in der Funktion "GetPrivateProfileString()" mit NULL angibt, werden alle vorhandenen Schlüssel zurückgegeben.
Ist aber bei mir leider nicht so.

Mein Code:

```
[DllImport("kernel32.dll", EntryPoint = "GetPrivateProfileStringW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, uint nSize, string lpFileName);
```
Meine Methode:

```
public int GetMyPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, ref string strBuffer, string lpFileName)
        {
            int iRet = 0;
            StringBuilder sbBuffer = new StringBuilder(260);

            iRet = GetPrivateProfileString(lpAppName, lpKeyName, lpDefault, sbBuffer, (uint)sbBuffer.Capacity, lpFileName);

            strBuffer = sbBuffer.ToString();

            return iRet;
        }
```
Und der Funktionsaufruf:

```
dll.GetMyPrivateProfileString("MESSAGETYPESETTING", null, "", ref strKeys, strConfigFilePath);
```
Die *.ini - Datei findet ihr im anhang, gezippt.
Kann mir jemand sagen wo das Problem liegen könnte.

Vielen Dank.
Grüße von CopWorker


----------



## Zvoni (19. April 2021)

Wieso benutzt du nicht die "richtige" Funktion?

GetPrivateProfileSection function (winbase.h) - Win32 apps

Zu deinem Problem: Ich glaube die Verwendung von "null" als zweiter Parameter ist es. In Beispielen habe ich überall "0" anstatt "null" gesehen.

EDIT: Quark. Das "null" ist richtig.
Wie handelt c# wenn man NULL an einen String sendet?
Die API erwartet einen NULL-Zeiger, du schickst aber "null" an deine Funktion im Parameter, welches ein String ist. Wandelt C# dieses NULL vielleicht in ein "" um?

EDIT2: Kannst du mal testhalber in deinem zweiten Code-Block ("Methode") in Zeile 6 anstatt lpKeyName direkt NULL eingeben/hart-coden und den code laufen lassen? Zeile 6 ist doch der API-Aufruf, ne?
Auf die Art kannst du herausfinden, ob der API-Call an sich schon korrekt ist.


----------



## CopWorker (19. April 2021)

Hallo Zvoni, 

bei  der Methode GetPrivateProfileSection function (winbase.h) - Win32 apps 
tritt das gleiche Problem auf. 
Es wird immer nur der erste Schlüsselname eingelesen.
Das ist schon klar, weil dann die logische 0 im Buffer wirkt und den String beendet.
Der Buffer ist nämlich vom Typ "StringBuilder".
Aber wie soll das sonst funktionieren. C++ verwendet ein char[] Buffer. 
Kann ich leider nicht einbinden, oder zumindest weiß ich nicht wie das gehen soll.


```
[DllImport("kernel32.dll", EntryPoint = "GetPrivateProfileSectionW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int GetPrivateProfileSection(string lpAppName, StringBuilder lpReturnedString, uint nSize, string lpFileName);
```


```
public int GetMyPrivateProfileSection(string strSection, ref string strBuffer, string strFilePath)
        {
            int iRet = 0;
            StringBuilder sbBuffer = new StringBuilder(260);

            iRet = GetPrivateProfileSection(strSection, sbBuffer, (uint)sbBuffer.Capacity, strFilePath);

            strBuffer = sbBuffer.ToString();

            return iRet;
        }
```

Vielleich hat jemand einen Tipp für mich.
Grüße von CopWorker


----------



## Zvoni (19. April 2021)

Aus dem Ur-Post geht aber nicht hervor, dass du der Meinung bist, "nur" den ersten Wert zurück zu bekommen. Dein Ur-Post impliziert, dass du gar nix zurückbekommst.

OK, wenn ich die API-Beschreibung richtig verstehe:


> The data in the buffer pointed to by the _lpReturnedString_ parameter consists of one or more null-terminated strings, followed by a final null character.


Heisst: Du bekommst einen String zurück, in welchem nach jedem Schlüssel-Wert-Paar ein Chr(0) ist, abgeschlossen mit einem zusätzlichen Chr(0).
Bedeutet: Deine ToString-Methode macht dir einen String durch die Rechnung (bzw. das Anzeigen des Strings), weil diese beim ersten Chr(0) aussteigt.

Was du noch "zusätzlich" brauchst, ist ein Split. in deinem Fall also ein
sbBuffer.ToString().Split("\0");
und strBuffer sollte ein Array sein
StringBuilder to Array of Strings how to convert


----------



## CopWorker (19. April 2021)

Zvoni hat gesagt.:


> Bedeutet: Deine ToString-Methode macht dir einen String durch die Rechnung (bzw. das Anzeigen des Strings), weil diese beim ersten Chr(0) aussteigt.


Das Problem tritt bereits vorher auf. 
Der StringBuilder Variable "sbBuffer" beendet die Zeichenkette nach Auftreten der ersten logischen 0.

Grüße von CopWorker.
Ich mach Feierabend, mir raucht der Kopf.


----------



## Spyke (20. April 2021)

schau mal hier
pinvoke.net:             GetPrivateProfileSection (kernel32)

da wird die Methode mit IntPtr deklariert und dann mit Marshal.PtrToStringAnsi ausgelesen


ansosnten alternativ die Datei selbst einlesen, ist ja nur ne textdatei
wenn eine Zeile mit [ beginnt und mit ] endet ist es der beginn einer Sektion
das dann solange sammeln was danach kommt bis eine neue Sektion kommt doer das ende der Datei erreicht wurde
ansosnten muss man bei eienr Ini-Datei nur wissen das wenn eine Zeile mit ; (Semikolon) beginnt ist es ein Kommentar
ansosnten die einzelnen Schlüssel mit ihren Wert einfach das erste Vorkommen eines = (Gleichheitszeichens) in einer Zeile suchen, was dann davor ist ist der Schlüssel und danach der Wert
Gibt es kein = in einer Zeile ist es eifnach nur so ein Schlüssel


----------



## Zvoni (20. April 2021)

Sicher?
Alle Beispiele, die ich gesehen habe, benutzen definitiv den StringBuilder, und dann das Verfahren mit dem Split.
Es ist nur die Anzeige des Strings, welches beim ersten 0 stoppt.
Kopier mal den Inhalt vom StringBuilder in ein Byte-Array, dann siehst du es.

Und wie Spyke es sagt: Eine INI ist ne simple Text-Datei, die relativ einfach auszulesen ist


----------



## CopWorker (20. April 2021)

Hallo Zvoni, 



Zvoni hat gesagt.:


> Es ist nur die Anzeige des Strings, welches beim ersten 0 stoppt.


Das sieht im Arbeitsspeicher aber so aus als würden definitiv nur der erste Schlüssel der Sektion eingelesen. 
Sonst käme ja nach dem ersten Schlüssel eine logische 0 und dann weitere Ascii Zeichen, dann wieder eine logische 0, usw..
Ich versuch´s mal damit: pinvoke.net: GetPrivateProfileSection (kernel32)

Trotzdem vielen Dank.

Grüße von CopWorker


----------



## CopWorker (23. April 2021)

Hallo Zvoni, hallo Spyke, 

das mit pinvoke.net: GetPrivateProfileSection (kernel32)
funktioniert genial. So soll Doku sein. Die ist nicht vom Microsoft. 
Weil dann würd´ ich das nicht verstehen.

Also alles gut. Läuft.
Vielen Dank dafür.

Und noch was:
Ich schreibe Texte in eine Textbox. 
Wenn neue Texte anliegen füge ich diese der Textbox an.

```
private void SetTraceLogMsg2TextboxScroll(string strMessage)
        {
            //Message for "tbReceiveMsgScroll"
            if (this.tbReceiveMsgScroll.InvokeRequired)
            {
                MethodInvoker del = delegate { SetTraceLogMsg2TextboxScroll(strMessage); };
                this.tbReceiveMsgScroll.Invoke(del);
            }
            else
                this.tbReceiveMsgScroll.AppendText((tbReceiveMsgScroll.Lines.Length > 0 ? "\r\n" : "") + strMessage);
        }
```
Leider flackern die Inhalte der Textbox stark. 
Manche Events stammen aus einem anderen Thread, drum habe ich den "Invoker" eingebaut.
Kann man das nicht ünterdrücken?

Vielen Dank.
Grüße von CopWorker


----------



## Spyke (26. April 2021)

könntest alternativ mal die RichTextBox probieren ob die auch flackert,
oder mit nem Label probiern

Nachtrag: 
die Abfrage von Lines ist glaube auch ein ziemlicher performance killer
prüf ev. eher auf
!string.IsNullOrEmpty(tbReceiveMsgScroll.Text)


----------

