BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Input / Output
.NET 1.1+

Reading and Writing INI Files

Initialisation files known as INI files provide a standard means for storing configuration information for software in a text file. Although rarely used by .NET applications, there are situations where these files must be read and written to using C#.

IniFile Class

We can now create the IniFile class, which will provide simple access to INI files without knowledge of the API functions. Add a new class to the project, naming it "IniFile", and add the following using directives. These will be used to handle exceptions from the API functions.

using System.ComponentModel;
using System.Runtime.InteropServices;

The IniFile class should be made public.

public class IniFile
{
}

Adding the Constructor

The IniFile class will represent a single INI file. We could hold the path and filename in a property and allow it to be changed. In this case we'll set the filename using the constructor and never change it again. Users of the class that wish to access multiple files will need to create more than one instance.

To create the constructor and the string variable that will hold the filename, add the following code to the class:

string _iniFile;

public IniFile(string fileName)
{
    _iniFile = fileName;
}

Creating the WriteValue Method

We will create four public methods within the IniFile class. The first is used to write values to an INI file. In the code below you can see that three parameters are used to accept the section and key names and the value to store. These values are used, along with the filename provided to the constructor, in the call to WritePrivateProfileString. If the API method encounters a problem, it will return a result of false. We check for this and throw a new exception with the message from the error.

public void WriteSetting(string section, string key, string value)
{
    if (NativeMethods.WritePrivateProfileString(section, key, value, _iniFile) == false)
    {
        throw new Win32Exception(Marshal.GetLastWin32Error());
    }
}

Creating the ReadValue Method

The second method, "ReadValue", reads a single value from the INI file. This method accepts four arguments to define the section and key to be read, the maximum length of the string to obtain and a default value to return if the desired key is not present. The first line of the method declares the string and sets its value to a number of spaces equal to the maximum length of the result. This string is passed to the GetPrivateProfileString method to act as a buffer to be filled.

The resultant value added to the string buffer by GetPrivateProfileString is a null-terminated string. This means that the final character value is zero, which in C# is represented by the control character, \0. If the result is smaller than the buffer size the C# string will contain the value, the null character and some spaces. To extract only the value, we split the string into an array wherever we find a null character. We then return the first item from the array.

The code for the method is as follows:

public string ReadSetting(string section, string key, uint maxLength, string defaultValue)
{
    string value = new string(' ', (int)maxLength);
    NativeMethods.GetPrivateProfileString(
        section, key, defaultValue, value, maxLength, _iniFile);
    return value.Split('\0')[0];
}

Creating the ReadSections Method

The third method uses GetPrivateProfileString again but this time to obtain a list of all of the sections that are defined in the INI file. To do so, we pass null to the lpAppName and lpKeyName parameters of the function. The result is a string containing a list of section names, each separated by a null character. The list is terminated with two null characters. This means we can split the string as before and return all but the final two results.

We'll be reusing the string-splitting functionality so in the code below it is defined in a private method:

public string[] ReadSections()
{
    string value = new string(' ', 65535);
    NativeMethods.GetPrivateProfileString(null, null, "\0", value, 65535, _iniFile);
    return SplitNullTerminatedStrings(value);
}

private static string[] SplitNullTerminatedStrings(string value)
{
    string[] raw = value.Split('\0');
    int itemCount = raw.Length - 2;
    string[] items = new string[itemCount];
    Array.Copy(raw, items, itemCount);
    return items;
}

Creating the ReadKeysInSection Method

The final public method is used to read all of the key names from an INI file section. The category to be read is passed to the lpAppName parameter of GetPrivateProfileString. The lpKeyName argument is set to null to indicate that we want the key names, rather than a single value. Again, the results are null-terminated with the final key name followed by two null characters so we can use the private method to process the result.

public string[] ReadKeysInSection(string section)
{
    string value = new string(' ', 65535);
    NativeMethods.GetPrivateProfileString(section, null, "\0", value, 65535, _iniFile);
    return SplitNullTerminatedStrings(value);
}

Using the IniFile Class

We can now use the IniFile class to create a new INI file and read and write values. The code below starts by creating a new file with the settings shown in the sample at the start of the article. It then reads a value that is present and one that is missing so must be substituted with a default value. Next, all of the sections are read, followed by all of the keys from the Colours section and an empty array of values from a non-existent section. Before executing the code, change the path for the INI file to a suitable location. The folder must exist but the file need not.

IniFile iniFile = new IniFile(@"c:\test\texteditor.ini");
iniFile.WriteSetting("Colours", "Background", "Black");
iniFile.WriteSetting("Colours", "Foreground", "White");
iniFile.WriteSetting("Font", "Face", "Arial");
iniFile.WriteSetting("Font", "Size", "12pt");

string foregroundColour = iniFile.ReadSetting("Colours", "Background", 32, "Red");
// Black

string unknownColour = iniFile.ReadSetting("Colours", "Highlight", 32, "Red");
// Red

string[] sections = iniFile.ReadSections();
// Colours,Font

string[] colours = iniFile.ReadKeysInSection("Colours");
// Background,Foreground

string[] unknownSection = iniFile.ReadKeysInSection("Unknown");
// Empty Array
8 December 2011