Índice de la sección dedicada a .NET (en el Guille) Cómo... en .NET

Manejar ficheros INI
Usando el API de Windows

Código para Visual Basic.NET (VB.NET)

Código para C Sharp (C#)


Publicado el 19/Ene/2004
Actualizado el 19/Ene/2004


En este ejemplo simplemente te mostraré una serie de funciones para manipular el contenido de los ficheros INI. Parte del código está basado en este ejemplo para Visual Basic clásico.

Todo el manejo de los ficheros INI se hace mediante una clase, en la que tenemos las siguientes funciones:

IniDeleteKey Borra una clave de un fichero INI
IniDeleteSection Borra una sección
IniWrite Guarda un valor en una clave de una sección
IniGet Lee el valor de una clave
IniGetSection Lee todo el contenido de una sección
IniGetSections Lee todas las secciones de un fichero INI
AppPath Devuelve el path del ejecutable
 

 

Las funciones del API usadas son las siguientes:

Para que pruebes el código, se adjunta una pequeña utilidad para leer las secciones y valores del fichero INI indicado, permitiendo modificar los valores, eliminarlos, etc. Por tanto si vas a hacer pruebas, te recomiendo que no las hagas con un fichero válido... no sea que el programa falle y me eches la bronca.

Esta es una captura del programa en ejecución:

Para ver el contenido de un fichero INI puedes hacerlo de dos formas:
Seleccionándolo con el botón examinar o bien arrastrándolo y soltándolo en el formulario (Drag & Drop)

 

Más abajo tienes el código de la clase y del formulario, tanto para Visual Basic .NET como para C#, y aquí tienes el zip con el contenido de los dos proyectos.

El código de Visual Basic .NET y C#: inisNET.zip 22.4 KB

 

Lo dicho en otras ocasiones: Espero que te sea de utilidad y si puedes... aporta tu granito de arena usando el botón PayPal. Gracias.

Nos vemos.
Guillermo


Código para Visual Basic.NET (VB.NET)El código para VB .NET

La clase cIniArray

'------------------------------------------------------------------------------
' Clase para manejar ficheros INIs
' Permite leer secciones enteras y todas las secciones de un fichero INI
'
' Última revisión:                                                  (04/Abr/01)
' Para usar con Visual Basic.NET                                    (21/Jul/02)
'
' ©Guillermo 'guille' Som, 1997-2002
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On 

Public Class cIniArray

    Private sBuffer As String ' Para usarla en las funciones GetSection(s)

    '--- Declaraciones para leer ficheros INI ---
    ' Leer todas las secciones de un fichero INI, esto seguramente no funciona en Win95
    ' Esta función no estaba en las declaraciones del API que se incluye con el VB
    Private Declare Function GetPrivateProfileSectionNames Lib "kernel32" Alias "GetPrivateProfileSectionNamesA" (ByVal lpszReturnBuffer As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer

    ' Leer una sección completa
    Private Declare Function GetPrivateProfileSection Lib "kernel32" Alias "GetPrivateProfileSectionA" (ByVal lpAppName As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer

    ' Leer una clave de un fichero INI
    Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer
    Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Integer, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer

    ' Escribir una clave de un fichero INI (también para borrar claves y secciones)
    Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal lpString As String, ByVal lpFileName As String) As Integer
    Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal lpString As Integer, ByVal lpFileName As String) As Integer
    Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Integer, ByVal lpString As Integer, ByVal lpFileName As String) As Integer

    Public Sub IniDeleteKey(ByVal sIniFile As String, ByVal sSection As String, Optional ByVal sKey As String = "")
        '--------------------------------------------------------------------------
        ' Borrar una clave o entrada de un fichero INI                  (16/Feb/99)
        ' Si no se indica sKey, se borrará la sección indicada en sSection
        ' En otro caso, se supone que es la entrada (clave) lo que se quiere borrar
        '
        ' Para borrar una sección se debería usar IniDeleteSection
        '
        If Len(sKey) = 0 Then
            ' Borrar una sección
            Call WritePrivateProfileString(sSection, 0, 0, sIniFile)
        Else
            ' Borrar una entrada
            Call WritePrivateProfileString(sSection, sKey, 0, sIniFile)
        End If
    End Sub

    Public Sub IniDeleteSection(ByVal sIniFile As String, ByVal sSection As String)
        '--------------------------------------------------------------------------
        ' Borrar una sección de un fichero INI                          (04/Abr/01)
        ' Borrar una sección
        Call WritePrivateProfileString(sSection, 0, 0, sIniFile)
    End Sub

    Public Function IniGet(ByVal sFileName As String, ByVal sSection As String, ByVal sKeyName As String, Optional ByVal sDefault As String = "") As String
        '--------------------------------------------------------------------------
        ' Devuelve el valor de una clave de un fichero INI
        ' Los parámetros son:
        '   sFileName   El fichero INI
        '   sSection    La sección de la que se quiere leer
        '   sKeyName    Clave
        '   sDefault    Valor opcional que devolverá si no se encuentra la clave
        '--------------------------------------------------------------------------
        Dim ret As Integer
        Dim sRetVal As String
        '
        sRetVal = New String(Chr(0), 255)
        '
        ret = GetPrivateProfileString(sSection, sKeyName, sDefault, sRetVal, Len(sRetVal), sFileName)
        If ret = 0 Then
            Return sDefault
        Else
            Return Left(sRetVal, ret)
        End If
    End Function

    Public Sub IniWrite(ByVal sFileName As String, ByVal sSection As String, ByVal sKeyName As String, ByVal sValue As String)
        '--------------------------------------------------------------------------
        ' Guarda los datos de configuración
        ' Los parámetros son los mismos que en LeerIni
        ' Siendo sValue el valor a guardar
        '
        Call WritePrivateProfileString(sSection, sKeyName, sValue, sFileName)
    End Sub

    Public Function IniGetSection(ByVal sFileName As String, ByVal sSection As String) As String()
        '--------------------------------------------------------------------------
        ' Lee una sección entera de un fichero INI                      (27/Feb/99)
        ' Adaptada para devolver un array de string                     (04/Abr/01)
        '
        ' Esta función devolverá un array de índice cero
        ' con las claves y valores de la sección
        '
        ' Parámetros de entrada:
        '   sFileName   Nombre del fichero INI
        '   sSection    Nombre de la sección a leer
        ' Devuelve:
        '   Un array con el nombre de la clave y el valor
        '   Para leer los datos:
        '       For i = 0 To UBound(elArray) -1 Step 2
        '           sClave = elArray(i)
        '           sValor = elArray(i+1)
        '       Next
        '
        Dim aSeccion() As String
        Dim n As Integer
        '
        ReDim aSeccion(0)
        '
        ' El tamaño máximo para Windows 95
        sBuffer = New String(ChrW(0), 32767)
        '
        n = GetPrivateProfileSection(sSection, sBuffer, sBuffer.Length, sFileName)
        '
        If n > 0 Then
            '
            ' Cortar la cadena al número de caracteres devueltos
	    ' menos los dos últimos que indican el final de la cadena
            sBuffer = sBuffer.Substring(0, n - 2).TrimEnd()
            ' Cada elemento estará separado por un Chr(0)
            ' y cada valor estará en la forma: clave = valor
            aSeccion = sBuffer.Split(New Char() {ChrW(0), "="c})
        End If
        ' Devolver el array
        Return aSeccion
    End Function

    Public Function IniGetSections(ByVal sFileName As String) As String()
        '--------------------------------------------------------------------------
        ' Devuelve todas las secciones de un fichero INI                (27/Feb/99)
        ' Adaptada para devolver un array de string                     (04/Abr/01)
        '
        ' Esta función devolverá un array con todas las secciones del fichero
        '
        ' Parámetros de entrada:
        '   sFileName   Nombre del fichero INI
        ' Devuelve:
        '   Un array con todos los nombres de las secciones
        '   La primera sección estará en el elemento 1,
        '   por tanto, si el array contiene cero elementos es que no hay secciones
        '
        Dim n As Integer
        Dim aSections() As String
        '
        ReDim aSections(0)
        '
        ' El tamaño máximo para Windows 95
        sBuffer = New String(ChrW(0), 32767)
        '
        ' Esta función del API no está definida en el fichero TXT
        n = GetPrivateProfileSectionNames(sBuffer, sBuffer.Length, sFileName)
        '
        If n > 0 Then
            ' Cortar la cadena al número de caracteres devueltos
	    ' menos los dos últimos que indican el final de la cadena
            sBuffer = sBuffer.Substring(0, n - 2).TrimEnd()
            aSections = sBuffer.Split(ChrW(0))
        End If
        ' Devolver el array
        Return aSections
    End Function
    '
    Public Shared Function AppPath( _
            Optional ByVal backSlash As Boolean = False _
            ) As String
        ' System.Reflection.Assembly.GetExecutingAssembly...
        Dim s As String = _
            IO.Path.GetDirectoryName( _
            System.Reflection.Assembly.GetExecutingAssembly.GetCallingAssembly.Location)
        ' si hay que añadirle el backslash
        If backSlash Then
            s &= "\"
        End If
        Return s
    End Function
End Class

...

El formulario

Public Class frmProfileIni
    Inherits System.Windows.Forms.Form
    '
    Private inicializando As Boolean = True
    '
    '
    Public Sub New()
        MyBase.New()
        '
        Application.EnableVisualStyles()
        '
        'El Diseñador de Windows Forms requiere esta llamada.
        InitializeComponent()
    End Sub
    '
#Region "Código generado por el Diseñador de Windows Forms "
    '...
#End Region
    '
    '
    Private mINI As New cIniArray
    '
    '------------------------------------------------------------------------------
    ' Procedimientos de eventos del formulario
    '------------------------------------------------------------------------------
    Private Sub cboSecciones_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboSecciones.SelectedIndexChanged, cboSecciones.TextChanged
        ' Mostrar las claves de esta sección
        If inicializando Then Exit Sub
        '
        Dim tContenidos() As String
        '
        tContenidos = mINI.IniGetSection(txtFicIni.Text, cboSecciones.Text)
        If tContenidos.Length > 0 Then
            cboClaves.Items.Clear()
            Dim i As Integer
            For i = 0 To tContenidos.Length - 1 Step 2
                cboClaves.Items.Add(tContenidos(i))
            Next
            txtValor.Text = ""
            cboClaves.SelectedIndex = 0
        End If
    End Sub
    '
    Private Sub cboClaves_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboClaves.SelectedIndexChanged, cboClaves.TextChanged
        If inicializando Then Exit Sub
        '
        cmdLeer_Click(cmdLeer, EventArgs.Empty)
    End Sub
    '
    Private Sub cmdAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdAdd.Click
        ' Añadir la sección, clave y/o valor
        '
        Dim sFicINI As String = txtFicIni.Text.Trim
        Dim sSeccion As String = cboSecciones.Text.Trim
        Dim sClave As String = cboClaves.Text.Trim
        Dim sValor As String = txtValor.Text.Trim
        '
        mINI.IniWrite(sFicINI, sSeccion, sClave, sValor)
    End Sub
    '
    Private Sub cmdBorrar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdBorrarClave.Click, cmdBorrarSeccion.Click
        ' Borrar sección o clave
        Dim sFicINI As String
        Dim sSeccion As String
        Dim sClave As String
        '
        sFicINI = txtFicIni.Text.Trim
        sSeccion = cboSecciones.Text.Trim
        sClave = cboClaves.Text.Trim
        '
        If CType(sender, Button).Name = "cmdBorrarSeccion" Then
            ' Borrar sección
            mINI.IniDeleteSection(sFicINI, sSeccion)
            ' Releer las secciones disponibles
            leerSecciones()
        Else
            ' Borrar clave
            mINI.IniDeleteKey(sFicINI, sSeccion, sClave)
            ' Leer las claves de esta sección
            cboSecciones_SelectedIndexChanged(cboSecciones, New System.EventArgs)
        End If
    End Sub
    '
    Private Sub cmdExaminar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdExaminar.Click
        ' Seleccionar el archivo a abrir o guardar
        Dim mCD As New OpenFileDialog
        '
        With mCD
            .Title = "Seleccionar archivos"
            .Filter = "Ficheros INIs (*.ini)|*.ini|Todos los Archivos (*.*)|*.*"
            .FileName = txtFicIni.Text.Trim
            If .ShowDialog = DialogResult.OK Then
                txtFicIni.Text = .FileName
                leerSecciones()
            End If
        End With
    End Sub
    '
    Private Sub cmdLeer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdLeer.Click
        ' Leer del fichero INI
        '
        Dim sFicINI As String = txtFicIni.Text.Trim
        Dim sSeccion As String = cboSecciones.Text.Trim
        Dim sClave As String = cboClaves.Text.Trim
        Dim sValor As String = txtValor.Text.Trim
        '
        txtValor.Text = mINI.IniGet(sFicINI, sSeccion, sClave, sValor)
    End Sub
    '
    Private Sub cmdSalir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSalir.Click
        Me.Close()
    End Sub
    '
    Private Sub frmProfileIni_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim sFicINI As String
        '
        sFicINI = Application.StartupPath & "\Prueba.ini"
        txtFicIni.Text = sFicINI
        mINI.IniWrite(sFicINI, "Seccion1", "Clave1", "Valor1")
        mINI.IniWrite(sFicINI, "Seccion1", "Clave2", "Valor2")
        mINI.IniWrite(sFicINI, "Seccion2", "Clave21", "Valor21")
        mINI.IniWrite(sFicINI, "Seccion2", "Clave22", "Valor22")
        mINI.IniWrite(sFicINI, "Seccion2", "Clave23", "Valor23")
        ' Añadir algunas secciones que ya existen
        With cboSecciones
            .Items.Clear()
            .Items.Add("Seccion1")
            .Items.Add("Seccion2")
        End With
        '
        inicializando = False
        '
        cboSecciones.SelectedIndex = 0
    End Sub
    '
    Private Sub leerSecciones()
        Dim tContenidos() As String
        '
        inicializando = True
        '
        ' Llenar las secciones de este fichero
        tContenidos = mINI.IniGetSections(txtFicIni.Text)
        If tContenidos.Length > 0 Then
            cboSecciones.Items.Clear()
            Dim i As Integer
            For i = 0 To tContenidos.Length - 1
                If Not tContenidos(i) Is Nothing Then
                    cboSecciones.Items.Add(tContenidos(i))
                End If
            Next
            txtValor.Text = ""
            cboSecciones.SelectedIndex = 0
        End If
        inicializando = False
    End Sub
    '
    Private Sub txtFicIni_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles txtFicIni.DragEnter, MyBase.DragEnter
        If e.Data.GetDataPresent(DataFormats.FileDrop) Then
            e.Effect = DragDropEffects.Copy
        End If
    End Sub
    '
    Private Sub txtFicIni_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles txtFicIni.DragDrop, MyBase.DragDrop
        If e.Data.GetDataPresent(DataFormats.FileDrop) Then
            txtFicIni.Text = CType(e.Data.GetData(DataFormats.FileDrop, True), String())(0)
            leerSecciones()
        End If
    End Sub
End Class

 


Código para C Sharp (C#)El código para C#

La clase cIniArray

//------------------------------------------------------------------------------
// Clase para manejar ficheros INIs
// Permite leer secciones enteras y todas las secciones de un fichero INI
//
// Última revisión:                                                  (04/Abr/01)
// Para usar con Visual Basic.NET                                    (21/Jul/02)
//
// Cpnvertida a C#                                                    (19/Ene/04)
//
// ©Guillermo 'guille' Som, 1997-2004
//------------------------------------------------------------------------------
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

public class cIniArray
{

    private string sBuffer; // Para usarla en las funciones GetSection(s)

    //--- Declaraciones para leer ficheros INI ---
    // Leer todas las secciones de un fichero INI, esto seguramente no funciona en Win95
    // Esta función no estaba en las declaraciones del API que se incluye con el VB
    [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
    public static extern int GetPrivateProfileSectionNames(
        string lpszReturnBuffer,  // address of return buffer
        int    nSize,             // size of return buffer
        string lpFileName         // address of initialization filename
    );
    // Leer una sección completa
    [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
    public static extern int GetPrivateProfileSection(
        string lpAppName,         // address of section name
        string lpReturnedString,  // address of return buffer
        int    nSize,             // size of return buffer
        string lpFileName         // address of initialization filename
    );
    // Leer una clave de un fichero INI
    [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
    public static extern int GetPrivateProfileString(
        string  lpAppName,        // points to section name
        string  lpKeyName,        // points to key name
        string  lpDefault,        // points to default string
        string  lpReturnedString, // points to destination buffer
        int     nSize,            // size of destination buffer
        string  lpFileName        // points to initialization filename
    );
    [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
    public static extern int GetPrivateProfileString(
        string  lpAppName,        // points to section name
        int     lpKeyName,        // points to key name
        string  lpDefault,        // points to default string
        string  lpReturnedString, // points to destination buffer
        int     nSize,            // size of destination buffer
        string  lpFileName        // points to initialization filename
        );
    // Escribir una clave de un fichero INI (también para borrar claves y secciones)
    [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
    public static extern int WritePrivateProfileString(
        string  lpAppName,  // pointer to section name
        string  lpKeyName,  // pointer to key name
        string  lpString,   // pointer to string to add
        string  lpFileName  // pointer to initialization filename
    );
    [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
    public static extern int WritePrivateProfileString(
        string  lpAppName,  // pointer to section name
        string  lpKeyName,  // pointer to key name
        int     lpString,   // pointer to string to add
        string  lpFileName  // pointer to initialization filename
        );
    [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
    public static extern int WritePrivateProfileString(
        string  lpAppName,  // pointer to section name
        int     lpKeyName,  // pointer to key name
        int     lpString,   // pointer to string to add
        string  lpFileName  // pointer to initialization filename
        );
    //
    public void IniDeleteKey(string sIniFile, string sSection, string sKey)
    {
        //--------------------------------------------------------------------------
        // Borrar una clave o entrada de un fichero INI                  (16/Feb/99)
        // Si no se indica sKey, se borrará la sección indicada en sSection
        // En otro caso, se supone que es la entrada (clave) lo que se quiere borrar
        //
        // Para borrar una sección se debería usar IniDeleteSection
        //
        if( sKey == "" )
        {
            // Borrar una sección
            WritePrivateProfileString(sSection, 0, 0, sIniFile);
        }
        else
        {
            // Borrar una entrada
            WritePrivateProfileString(sSection, sKey, 0, sIniFile);
        }
    }  
    public void IniDeleteSection(string sIniFile, string sSection)
    {
        //--------------------------------------------------------------------------
        // Borrar una sección de un fichero INI                          (04/Abr/01)
        // Borrar una sección
        WritePrivateProfileString(sSection, 0, 0, sIniFile);
    }  
    public string  IniGet(string sFileName, string sSection, string sKeyName, string sDefault)
    {
        //--------------------------------------------------------------------------
        // Devuelve el valor de una clave de un fichero INI
        // Los parámetros son:
        //   sFileName   El fichero INI
        //   sSection    La sección de la que se quiere leer
        //   sKeyName    Clave
        //   sDefault    Valor opcional que devolverá si no se encuentra la clave
        //--------------------------------------------------------------------------
        int ret;
        string sRetVal;
        //
        sRetVal = new string(' ', 255);
        //
        ret = GetPrivateProfileString(sSection, sKeyName, sDefault, sRetVal, sRetVal.Length, sFileName);
        if( ret == 0 )
        {
            return sDefault;
        }
        else
        {
            return sRetVal.Substring(0, ret);
        }
    }  
    public void IniWrite(string sFileName, string sSection, string sKeyName, string sValue)
    {
        //--------------------------------------------------------------------------
        // Guarda los datos de configuración
        // Los parámetros son los mismos que en LeerIni
        // Siendo sValue el valor a guardar
        //
        WritePrivateProfileString(sSection, sKeyName, sValue, sFileName);
    }  
    public string[] IniGetSection(string sFileName, string sSection)
    {
        //--------------------------------------------------------------------------
        // Lee una sección entera de un fichero INI                      (27/Feb/99)
        // Adaptada para devolver un array de string                     (04/Abr/01)
        //
        // Esta función devolverá un array de índice cero
        // con las claves y valores de la sección
        //
        // Parámetros de entrada:
        //   sFileName   Nombre del fichero INI
        //   sSection    Nombre de la sección a leer
        // Devuelve:
        //   Un array con el nombre de la clave y el valor
        //   Para leer los datos:
        //       For i = 0 To UBound(elArray) -1 Step 2
        //           sClave = elArray(i)
        //           sValor = elArray(i+1)
        //       Next
        //
        string[] aSeccion;
        int n;
        //
        aSeccion = new string[0];
        //
        // El tamaño máximo para Windows 95
        sBuffer = new string('\0', 32767);
        //
        n = GetPrivateProfileSection(sSection, sBuffer, sBuffer.Length, sFileName);
        //
        if( n > 0 )
        {
            // Cortar la cadena al número de caracteres devueltos
	    // menos los dos últimos que indican el final de la cadena
            sBuffer = sBuffer.Substring(0, n - 2).TrimEnd();
            //
            // Cada una de las entradas estará separada por un Chr$(0)
            // y cada valor estará en la forma: clave = valor
            aSeccion = sBuffer.Split(new char[]{'\0', '='});
        }
        // Devolver el array
        return aSeccion;
    }  
    public string[] IniGetSections(string sFileName)
    {
        //--------------------------------------------------------------------------
        // Devuelve todas las secciones de un fichero INI                (27/Feb/99)
        // Adaptada para devolver un array de string                     (04/Abr/01)
        //
        // Esta función devolverá un array con todas las secciones del fichero
        //
        // Parámetros de entrada:
        //   sFileName   Nombre del fichero INI
        // Devuelve:
        //   Un array con todos los nombres de las secciones
        //   La primera sección estará en el elemento 1,
        //   por tanto, si el array contiene cero elementos es que no hay secciones
        //
        int n;
        string[] aSections;
        //
        aSections = new string[0];
        //
        // El tamaño máximo para Windows 95
        sBuffer = new string('\0', 32767);
        //
        // Esta función del API no está definida en el fichero TXT
        n = GetPrivateProfileSectionNames(sBuffer, sBuffer.Length, sFileName);
        //
        if( n > 0 )
        {
            // Cortar la cadena al número de caracteres devueltos
	    // menos los dos últimos que indican el final de la cadena
            sBuffer = sBuffer.Substring(0, n - 2).TrimEnd();
            aSections = sBuffer.Split('\0');
        }
        // Devolver el array
        return aSections;
    }  
}

...

El formulario

public class fIniArrayCS : System.Windows.Forms.Form
{
    //
    #region Código generado por el Diseñador de Windows Forms

    //
    private bool inicializando  = true;
    //
    /// 
    /// Punto de entrada principal de la aplicación.
    /// 
    [STAThread]
    static void Main() 
    {
        Application.EnableVisualStyles(); 
        Application.Run(new fIniArrayCS());
    }
    //
    //
    private cIniArray mINI = new cIniArray();
    //
    private void leerSecciones()
    {
        string[] tContenidos;
        //
        inicializando = true;
        //
        // Llenar las secciones de este fichero
        tContenidos = mINI.IniGetSections(txtFicIni.Text);
        if( tContenidos.Length > 0 )
        {
            cboSecciones.Items.Clear();
            int i;
            for(i = 0; i < tContenidos.Length; i++)
            {
                if( tContenidos[i] != null )
                {
                    cboSecciones.Items.Add(tContenidos[i]);
                }
            }
            txtValor.Text = "";
            cboSecciones.SelectedIndex = 0;
        }
        inicializando = false;
    }  
    //
    //------------------------------------------------------------------------------
    // Procedimientos de eventos del formulario
    //------------------------------------------------------------------------------
    private void cboSecciones_SelectedIndexChanged(System.Object  sender, System.EventArgs  e) 
    {
        // Mostrar las claves de esta sección
        if( inicializando ) return;
        //
        string[] tContenidos;
        //
        tContenidos = mINI.IniGetSection(txtFicIni.Text, cboSecciones.Text);
        if( tContenidos.Length > 0 )
        {
            cboClaves.Items.Clear();
            int i;
            for(i = 0; i < tContenidos.Length; i += 2)
            {
                cboClaves.Items.Add(tContenidos[i]);
            }
            txtValor.Text = "";
            cboClaves.SelectedIndex = 0;
        }
    }  
    //
    private void cboClaves_SelectedIndexChanged(System.Object  sender, System.EventArgs  e) 
    {
        if( inicializando ) return;
        //
        cmdLeer_Click(cmdLeer, EventArgs.Empty);
    }  
    //
    private void cmdAdd_Click(System.Object  sender, System.EventArgs  e) 
    {
        // Añadir la sección, clave y/o valor
        //
        string sFicINI  = txtFicIni.Text.Trim();
        string sSeccion  = cboSecciones.Text.Trim();
        string sClave  = cboClaves.Text.Trim();
        string sValor  = txtValor.Text.Trim();
        //
        mINI.IniWrite(sFicINI, sSeccion, sClave, sValor);
    }  
    //
    private void cmdBorrarSeccion_Click(System.Object  sender, System.EventArgs  e) 
    {
        // Borrar sección o clave
        string sFicINI;
        string sSeccion;
        string sClave;
        //
        sFicINI = txtFicIni.Text.Trim();
        sSeccion = cboSecciones.Text.Trim();
        sClave = cboClaves.Text.Trim();
        //
        if( (( Button)sender).Name == "cmdBorrarSeccion" )
        {
            // Borrar sección
            mINI.IniDeleteSection(sFicINI, sSeccion);
            // Releer las secciones disponibles
            leerSecciones();
        }
        else
        {
            // Borrar clave
            mINI.IniDeleteKey(sFicINI, sSeccion, sClave);
            // Leer las claves de esta sección
            cboSecciones_SelectedIndexChanged(cboSecciones, System.EventArgs.Empty);
        }
    }  
    //
    private void cmdExaminar_Click(System.Object  sender, System.EventArgs  e) 
    {
        // Seleccionar el archivo a abrir o guardar
        OpenFileDialog mCD = new  OpenFileDialog();
        //
        mCD.Title = "Seleccionar archivos";
        mCD.Filter = "Ficheros INIs (*.ini)|*.ini|Todos los Archivos (*.*)|*.*";
        mCD.FileName = txtFicIni.Text.Trim();
        if( mCD.ShowDialog() == DialogResult.OK )
        {
            txtFicIni.Text = mCD.FileName;
            leerSecciones();
        }
    }  
    //
    private void cmdLeer_Click(System.Object  sender, System.EventArgs  e) 
    {
        // Leer del fichero INI
        //
        string sFicINI  = txtFicIni.Text.Trim();
        string sSeccion  = cboSecciones.Text.Trim();
        string sClave  = cboClaves.Text.Trim();
        string sValor  = txtValor.Text.Trim();
        //
        txtValor.Text = mINI.IniGet(sFicINI, sSeccion, sClave, sValor);
    }  
    //
    private void cmdSalir_Click(System.Object  sender, System.EventArgs  e) 
    {
        this.Close();
    }  
    //
    private void fIniArrayCS_Load(System.Object  sender, System.EventArgs  e) 
    {
        string sFicINI;
        //
        sFicINI = Application.StartupPath + @"\Prueba.ini";
        txtFicIni.Text = sFicINI;
        mINI.IniWrite(sFicINI, "Seccion1", "Clave1", "Valor1");
        mINI.IniWrite(sFicINI, "Seccion1", "Clave2", "Valor2");
        mINI.IniWrite(sFicINI, "Seccion2", "Clave21", "Valor21");
        mINI.IniWrite(sFicINI, "Seccion2", "Clave22", "Valor22");
        mINI.IniWrite(sFicINI, "Seccion2", "Clave23", "Valor23");
        // Añadir algunas secciones que ya existen
        cboSecciones.Items.Clear();
        cboSecciones.Items.Add("Seccion1");
        cboSecciones.Items.Add("Seccion2");
        //
        inicializando = false;
        //
        cboSecciones.SelectedIndex = 0;
    }  
    //
    private void fIniArrayCS_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
    {
        if( e.Data.GetDataPresent(DataFormats.FileDrop) )
        {
            e.Effect = DragDropEffects.Copy;
        }
    }
    private void fIniArrayCS_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
    {
        if( e.Data.GetDataPresent(DataFormats.FileDrop) )
        {
            txtFicIni.Text = (( String[])e.Data.GetData(DataFormats.FileDrop, true))[0];
            leerSecciones();
        }
    }
}

...


la Luna del Guille o... el Guille que está en la Luna... tanto monta...