雖然 .NET 推薦使用 app.config / appsettings.json,但在輕量工具、舊專案遷移、跨語言共享配置等場景下,INI 以其純文本、結構簡單、無依賴的特性仍具備不可替代的優勢。本文封裝一個開箱即用的 IniFile 類,底層調用 kernel32.dll 的 WritePrivateProfileString / GetPrivateProfileString,支援 Section/Key 結構、預設 Section、存在性檢查,並提供現代化調用範例。
[SectionName]
Key1=Value1
Key2=Value2
[AnotherSection]
IP=127.0.0.1
Port=8080
[ ] 包裹,分組用途。; 或 # 開頭(WinAPI 會自動忽略)。using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
public class IniFile
{
public string Path { get; }
[DllImport("kernel32", CharSet = CharSet.Unicode)]
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
[DllImport("kernel32", CharSet = CharSet.Unicode)]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
///
/// 建構函式,預設當前目錄下 default.ini
///
public IniFile(string iniPath = ".\\default.ini") => Path = Path.GetFullPath(iniPath);
/// 寫入指定 Section/Key
public void Write(string section, string key, string value) =>
WritePrivateProfileString(section, key, value, Path);
/// 寫入預設 Section (default)
public void Write(string key, string value) =>
WritePrivateProfileString("default", key, value, Path);
/// 讀取指定 Section/Key,不存在回傳空字串
public string Read(string section, string key)
{
var sb = new StringBuilder(256);
GetPrivateProfileString(section, key, "", sb, 256, Path);
return sb.ToString();
}
/// 讀取預設 Section (default)
public string Read(string key) => Read("default", key);
/// 檢查文件是否存在
public bool Exists() => File.Exists(Path);
}
class Program
{
static void Main()
{
// 1. 實例化(支援相對/絕對路徑)
var ini = new IniFile(@".\config\app.ini");
// 2. 寫入配置
ini.Write("Database", "Host", "127.0.0.1");
ini.Write("Database", "Port", "3306");
ini.Write("Database", "User", "root");
ini.Write("Database", "Pwd", "secret");
ini.Write("App", "Version", "1.2.3");
ini.Write("App", "AutoUpdate", "true");
// 3. 讀取配置(強型別轉換自行處理)
string host = ini.Read("Database", "Host");
int port = int.Parse(ini.Read("Database", "Port"));
bool autoUpdate = bool.Parse(ini.Read("App", "AutoUpdate"));
Console.WriteLine($"連線 {host}:{port} | 自動更新:{autoUpdate}");
// 4. 預設 Section 簡寫
ini.Write("LastLogin", "user001"); // 寫入 [default] LastLogin=user001
string last = ini.Read("LastLogin"); // 讀取 [default] LastLogin
// 5. 檢查文件存在
if (!ini.Exists()) Console.WriteLine("配置文件遺失,將建立預設值。");
Console.ReadKey();
}
}
預設 StringBuilder(256) 足夠大多數場景。若值可能超過 255 字元,請擴大容量並傳入 size 參數;WinAPI 會在緩衝區不足時截斷,不會拋出異常。
CharSet = CharSet.Unicode 確保中文、特殊字元正確讀寫。若需相容 ANSI 版本(極舊系統),可改 CharSet.Ansi 並儲存為 ANSI 編碼檔案。
WinAPI 函式本身是執行緒安全的,但同一進程多執行緒高頻寫入同一檔案可能導致內容交錯。建議在上層加 lock 或使用單例模式集中管理。
每次讀寫皆觸發磁碟 I/O。頻繁操作時,建議:
Read 所有鍵值快取到記憶體 Dictionary。Write 回檔。| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| INI + WinAPI | 零依賴、跨語言、人類可讀 | 無型別、無巢狀、頻繁 I/O 慢 | 輕量工具、啟動參數、跨進程共享 |
| XML / JSON | 結構豐富、生態完善 | 需序列化庫、檔案較大 | 現代 .NET 應用標配 |
| Registry | 系統級、權限隔離 | 寫入需管理員、難備份遷移 | 系統級設定、文件關聯 |
IniFile 類以 50 行核心代碼封裝了 Win32 API 的讀寫細節,提供直觀的 Write/Read 介面,開箱即用。若專案僅需簡單配置持久化,它是比引入 JSON 庫更輕量的選擇;若配置結構複雜、需型別安全,請毫猶豫遷移至 System.Text.Json + appsettings.json。選對工具,才能事半功倍。