Working with classic INI files in C# relies on native Windows functions: GetPrivateProfileString and WritePrivateProfileString. This article demonstrates a clean wrapper and a practical configuration manager built around them.
Sample INI structure
Assume a file named AppSettings.ini:
[FtpServer]
IP=127.0.0.1
Port=21
UserName=user
Password=user
Encapsulating the kernel32 calls
Create a static helper that exposes read and write operations with automatic type conversion.
public static class IniFile
{
[DllImport("kernel32", CharSet = CharSet.Unicode)]
private static extern long WritePrivateProfileString(
string section, string key, string val, string path);
[DllImport("kernel32", CharSet = CharSet.Unicode)]
private static extern int GetPrivateProfileString(
string section, string key, string def,
StringBuilder buffer, int size, string path);
public static string GetString(string section, string key, string fallback, string filePath)
{
var sb = new StringBuilder(512);
GetPrivateProfileString(section, key, fallback, sb, sb.Capacity, filePath);
return sb.ToString();
}
public static int GetInt(string section, string key, int fallback, string filePath)
{
var raw = GetString(section, key, fallback.ToString(), filePath);
return int.TryParse(raw, out int result) ? result : fallback;
}
public static void SetValue(string section, string key, string value, string filePath)
{
WritePrivateProfileString(section, key, value, filePath);
}
}
Thread-safe configuration manager
Below is a singleton that loads FTP settings on demand and allows udpating them while persisting changes back to disk.
internal sealed class AppConfiguration
{
public string FtpAddress { get; private set; }
public int FtpPort { get; private set; }
public string FtpUser { get; private set; }
public string FtpPass { get; private set; }
private readonly string _configPath =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AppSettings.ini");
private static readonly ILog Log =
LogManager.GetLogger(typeof(AppConfiguration));
public void Load()
{
try
{
FtpAddress = IniFile.GetString("FtpServer", "IP", "127.0.0.1", _configPath);
FtpPort = IniFile.GetInt("FtpServer", "Port", 21, _configPath);
FtpUser = IniFile.GetString("FtpServer", "UserName", "user", _configPath);
FtpPass = IniFile.GetString("FtpServer", "Password", "user", _configPath);
}
catch (Exception ex)
{
Log.Error($"INI load error: {ex.Message}");
}
}
public void UpdateFtp(string address, int port, string user, string pass)
{
FtpAddress = address;
FtpPort = port;
FtpUser = user;
FtpPass = pass;
try
{
IniFile.SetValue("FtpServer", "IP", address, _configPath);
IniFile.SetValue("FtpServer", "Port", port.ToString(), _configPath);
IniFile.SetValue("FtpServer", "UserName", user, _configPath);
IniFile.SetValue("FtpServer", "Password", pass, _configPath);
}
catch (Exception ex)
{
Log.Error($"FTP update failed: {ex.Message}");
throw;
}
}
#region Singleton
private static readonly object Padlock = new object();
private static AppConfiguration _instance;
private AppConfiguration() { }
public static AppConfiguration Current
{
get
{
if (_instance != null) return _instance;
lock (Padlock)
{
_instance = _instance ?? new AppConfiguration();
}
return _instance;
}
}
#endregion
}