Working with INI Files in C#

Understanding INI Files

INI (Initialization) files are a simple text-based format used to store configuration settings for applications. They are easily human-readable and editable, making them a convenient choice for storing settings that might need to be adjusted without recompiling the application. This tutorial will guide you through reading and writing INI files in C# using a custom implementation leveraging Windows P/Invoke. We’ll also touch upon alternative approaches and modern configuration methods.

The INI File Format

INI files consist of sections, keys, and values. A typical INI file looks like this:

[SectionName]
KeyName=Value
AnotherKey=AnotherValue

Sections are enclosed in square brackets ([]) and provide a way to organize keys. Keys and values are separated by an equals sign (=).

Implementing an INI File Reader/Writer

The .NET framework doesn’t provide a built-in class specifically for handling INI files. However, we can utilize Windows APIs through P/Invoke (Platform Invoke) to interact with INI files in a way similar to how legacy Windows applications do. This allows us to read and write INI files without relying on external libraries.

Here’s a IniFile class that provides the necessary functionality:

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

namespace MyProg
{
    class IniFile
    {
        string Path;
        string EXE = Assembly.GetExecutingAssembly().GetName().Name;

        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        static extern long WritePrivateProfileString(string Section, string Key, string Value, string FilePath);

        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        static extern int GetPrivateProfileString(string Section, string Key, string Default, StringBuilder RetVal, int Size, string FilePath);

        public IniFile(string IniPath = null)
        {
            Path = new FileInfo(IniPath ?? EXE + ".ini").FullName;
        }

        public string Read(string Key, string Section = null)
        {
            var RetVal = new StringBuilder(255);
            GetPrivateProfileString(Section ?? EXE, Key, "", RetVal, 255, Path);
            return RetVal.ToString();
        }

        public void Write(string Key, string Value, string Section = null)
        {
            WritePrivateProfileString(Section ?? EXE, Key, Value, Path);
        }

        public void DeleteKey(string Key, string Section = null)
        {
            Write(Key, null, Section ?? EXE);
        }

        public void DeleteSection(string Section = null)
        {
            Write(null, null, Section ?? EXE);
        }

        public bool KeyExists(string Key, string Section = null)
        {
            return Read(Key, Section).Length > 0;
        }
    }
}

Explanation:

  • Namespaces: We use System, System.IO, System.Reflection, System.Runtime.InteropServices, and System.Text to provide necessary functionalities.
  • P/Invoke: The DllImport attribute is used to import functions from the kernel32.dll Windows API. These functions (WritePrivateProfileString and GetPrivateProfileString) are responsible for writing and reading INI file data.
  • IniFile Class: This class encapsulates the INI file handling logic.
  • Constructor: The constructor takes an optional IniPath argument. If no path is provided, the INI file is created/opened in the same directory as the executable with the .ini extension.
  • Read() Method: This method reads a value from the INI file. It takes the key and an optional section as arguments.
  • Write() Method: This method writes a value to the INI file.
  • DeleteKey() and DeleteSection(): Methods to remove keys or sections.
  • KeyExists(): Checks if a key exists in a specified section.

Using the IniFile Class

Here’s how to use the IniFile class:

// Create or load an INI file (defaults to EXE.ini in the executable directory)
var MyIni = new IniFile();

// Or specify a specific file path
// var MyIni = new IniFile("Settings.ini");
// var MyIni = new IniFile(@"C:\Settings.ini");

// Write values to the INI file
MyIni.Write("DefaultVolume", "100");
MyIni.Write("HomePage", "http://www.google.com");

// Write values to a specific section
MyIni.Write("DefaultVolume", "100", "Audio");
MyIni.Write("HomePage", "http://www.google.com", "Web");

// Read values from the INI file
var DefaultVolume = MyIni.Read("DefaultVolume");
var HomePage = MyIni.Read("HomePage");

// Read values from a specific section
var AudioVolume = MyIni.Read("DefaultVolume", "Audio");
var WebPage = MyIni.Read("HomePage", "Web");

// Check if a key exists
if (!MyIni.KeyExists("DefaultVolume", "Audio"))
{
    MyIni.Write("DefaultVolume", "100", "Audio");
}

// Delete a key
MyIni.DeleteKey("DefaultVolume", "Audio");

// Delete a section
MyIni.DeleteSection("Web");

Alternative Approaches

  • NuGet Packages: There are several NuGet packages available that provide INI file parsing and writing functionality, such as INI Parser. These packages can simplify the process and offer more features.
  • .NET Configuration Files: For more complex application configurations, consider using .NET’s built-in configuration files (app.config or web.config). These files are XML-based and offer a more structured and robust approach to managing application settings.
  • JSON or YAML: For modern applications, consider using JSON or YAML configuration files. These formats are more flexible and easier to parse than INI files.

Best Practices

  • Error Handling: Implement error handling to gracefully handle situations where the INI file is not found or is corrupted.
  • File Locking: If multiple threads or processes access the INI file, implement file locking to prevent data corruption.
  • Configuration Management: For large applications, consider using a configuration management framework to manage application settings in a more organized and maintainable way.

Leave a Reply

Your email address will not be published. Required fields are marked *