本页说明在 .NET Framework 4.8 窗体(或控制台)项目中读写传统 INI 配置,并简要说明 INF 与 INI 的区别。目标框架以 LCode 默认的 .NET 4.8 为准;示例写法面向 C# 5.0 与 VB.NET(勿用 C# 6+ 语法如字符串插值、nameof)。操作文案以 LCode 中文界面为准。.NET 无内置 INI API,常用「自写小助手」或调用 Win32 Profile API。通用文本/流读写见 文件与流读写;结构化配置可改用 JSON 与 XML 解析

选用哪种方式

方式 依赖 适用
自写 INI 助手(推荐入门) System.IO(框架自带) 节/键/值结构简单、需跨平台或少依赖 Win32;完全托管,易调试
Win32 Profile API kernel32.dllGetPrivateProfileString / WritePrivateProfileString 需与旧工具/旧程序共用同一 INI 语义(节名、默认值、写回行为)时
第三方 INI 库 另引 DLL 复杂多编码/注释保留等需求;本页不展开。LCode 主界面 NuGet暂不支持,待更新完善

INI 文件长什么样

常见文本结构:方括号为节(Section),其下为 键=值。空行与以 ; 开头的行多为注释(自写助手可按需忽略)。

[App]
Title=Demo
Count=3

[Window]
Width=800
Height=600
提示:路径可用 Path.Combine(Application.StartupPath, "config.ini"),把 INI 放在输出目录旁。把文件加入项目时的「复制到输出目录」见 资源文件

在 LCode 中使用

窗体应用入门 建好项目后:

  1. 自写助手:新建类文件(如 IniHelper.cs / IniHelper.vb),粘贴下方示例。需 using System.IO; / Imports System.IO,一般不必额外添加引用。
  2. Win32:在类中声明 DllImport(C#)或 Declare/DllImport(VB)。非托管声明要点见 引用 DLL
  3. 菜单 编译 → 编译解决方案F8),确认无错误后运行测试读写。

自写 INI 助手 · 常用 API 思路

成员 说明
Read(section, key, defaultValue) 读指定节下的键;找不到则返回默认值
Write(section, key, value) 写入(或新建)节与键,再整文件写回磁盘
编码 示例用 UTF-8;若与旧 ANSI 工具共用,请改为约定编码(如 GB2312)
C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

public class IniHelper
{
    private readonly string _path;
    private readonly Encoding _enc;

    public IniHelper(string path)
    {
        _path = path;
        _enc = Encoding.UTF8;
    }

    public string Read(string section, string key, string defaultValue)
    {
        Dictionary<string, Dictionary<string, string>> data = Load();
        Dictionary<string, string> sec;
        if (!data.TryGetValue(section, out sec))
            return defaultValue;
        string val;
        if (!sec.TryGetValue(key, out val))
            return defaultValue;
        return val;
    }

    public void Write(string section, string key, string value)
    {
        Dictionary<string, Dictionary<string, string>> data = Load();
        Dictionary<string, string> sec;
        if (!data.TryGetValue(section, out sec))
        {
            sec = new Dictionary<string, string>(
                StringComparer.OrdinalIgnoreCase);
            data[section] = sec;
        }
        sec[key] = value == null ? "" : value;
        Save(data);
    }

    private Dictionary<string, Dictionary<string, string>> Load()
    {
        Dictionary<string, Dictionary<string, string>> data =
            new Dictionary<string, Dictionary<string, string>>(
                StringComparer.OrdinalIgnoreCase);
        if (!File.Exists(_path))
            return data;

        string current = null;
        string[] lines = File.ReadAllLines(_path, _enc);
        for (int i = 0; i < lines.Length; i++)
        {
            string line = lines[i].Trim();
            if (line.Length == 0 || line.StartsWith(";"))
                continue;
            if (line.StartsWith("[") && line.EndsWith("]"))
            {
                current = line.Substring(1, line.Length - 2).Trim();
                if (!data.ContainsKey(current))
                {
                    data[current] = new Dictionary<string, string>(
                        StringComparer.OrdinalIgnoreCase);
                }
                continue;
            }
            if (current == null)
                continue;
            int eq = line.IndexOf('=');
            if (eq <= 0)
                continue;
            string k = line.Substring(0, eq).Trim();
            string v = line.Substring(eq + 1).Trim();
            data[current][k] = v;
        }
        return data;
    }

    private void Save(Dictionary<string, Dictionary<string, string>> data)
    {
        StringBuilder sb = new StringBuilder();
        foreach (KeyValuePair<string, Dictionary<string, string>> sec in data)
        {
            sb.Append('[').Append(sec.Key).AppendLine("]");
            foreach (KeyValuePair<string, string> kv in sec.Value)
                sb.Append(kv.Key).Append('=').AppendLine(kv.Value);
            sb.AppendLine();
        }
        File.WriteAllText(_path, sb.ToString(), _enc);
    }
}

// 使用:
// string path = Path.Combine(Application.StartupPath, "config.ini");
// IniHelper ini = new IniHelper(path);
// ini.Write("App", "Title", "Demo");
// string title = ini.Read("App", "Title", "");
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Text

Public Class IniHelper
    Private ReadOnly _path As String
    Private ReadOnly _enc As Encoding

    Public Sub New(path As String)
        _path = path
        _enc = Encoding.UTF8
    End Sub

    Public Function Read(section As String, key As String,
                         defaultValue As String) As String
        Dim data = Load()
        Dim sec As Dictionary(Of String, String) = Nothing
        If Not data.TryGetValue(section, sec) Then
            Return defaultValue
        End If
        Dim val As String = Nothing
        If Not sec.TryGetValue(key, val) Then
            Return defaultValue
        End If
        Return val
    End Function

    Public Sub Write(section As String, key As String, value As String)
        Dim data = Load()
        Dim sec As Dictionary(Of String, String) = Nothing
        If Not data.TryGetValue(section, sec) Then
            sec = New Dictionary(Of String, String)(
                StringComparer.OrdinalIgnoreCase)
            data(section) = sec
        End If
        sec(key) = If(value, "")
        Save(data)
    End Sub

    Private Function Load() As Dictionary(Of String, Dictionary(Of String, String))
        Dim data As New Dictionary(Of String, Dictionary(Of String, String))(
            StringComparer.OrdinalIgnoreCase)
        If Not File.Exists(_path) Then
            Return data
        End If

        Dim current As String = Nothing
        Dim lines As String() = File.ReadAllLines(_path, _enc)
        For Each raw As String In lines
            Dim line As String = raw.Trim()
            If line.Length = 0 OrElse line.StartsWith(";") Then
                Continue For
            End If
            If line.StartsWith("[") AndAlso line.EndsWith("]") Then
                current = line.Substring(1, line.Length - 2).Trim()
                If Not data.ContainsKey(current) Then
                    data(current) = New Dictionary(Of String, String)(
                        StringComparer.OrdinalIgnoreCase)
                End If
                Continue For
            End If
            If current Is Nothing Then
                Continue For
            End If
            Dim eq As Integer = line.IndexOf("="c)
            If eq <= 0 Then
                Continue For
            End If
            Dim k As String = line.Substring(0, eq).Trim()
            Dim v As String = line.Substring(eq + 1).Trim()
            data(current)(k) = v
        Next
        Return data
    End Function

    Private Sub Save(data As Dictionary(Of String, Dictionary(Of String, String)))
        Dim sb As New StringBuilder()
        For Each sec In data
            sb.Append("["c).Append(sec.Key).AppendLine("]")
            For Each kv In sec.Value
                sb.Append(kv.Key).Append("="c).AppendLine(kv.Value)
            Next
            sb.AppendLine()
        Next
        File.WriteAllText(_path, sb.ToString(), _enc)
    End Sub
End Class

' 使用:
' Dim path As String = Path.Combine(Application.StartupPath, "config.ini")
' Dim ini As New IniHelper(path)
' ini.Write("App", "Title", "Demo")
' Dim title As String = ini.Read("App", "Title", "")

Win32 Profile API

调用 kernel32 的私有配置文件 API,行为接近经典 Windows INI。适合与旧程序共用文件;路径建议用绝对路径。

API 说明
GetPrivateProfileString 读节/键;找不到时返回传入的默认字符串
WritePrivateProfileString 写节/键;值为 null 时可按 API 约定删除键(慎用)
C#
using System;
using System.Runtime.InteropServices;
using System.Text;

public static class IniWin32
{
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetPrivateProfileString(
        string section, string key, string defaultValue,
        StringBuilder retVal, int size, string filePath);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    private static extern bool WritePrivateProfileString(
        string section, string key, string value, string filePath);

    public static string Read(string path, string section,
                              string key, string defaultValue)
    {
        StringBuilder sb = new StringBuilder(512);
        GetPrivateProfileString(section, key, defaultValue,
                                sb, sb.Capacity, path);
        return sb.ToString();
    }

    public static void Write(string path, string section,
                             string key, string value)
    {
        WritePrivateProfileString(section, key, value, path);
    }
}

// 使用:
// string path = Path.Combine(Application.StartupPath, "config.ini");
// IniWin32.Write(path, "App", "Title", "Demo");
// string title = IniWin32.Read(path, "App", "Title", "");
VB.NET
Imports System
Imports System.Runtime.InteropServices
Imports System.Text

Public Module IniWin32
    <DllImport("kernel32.dll", CharSet:=CharSet.Unicode)>
    Private Function GetPrivateProfileString(
        section As String, key As String, defaultValue As String,
        retVal As StringBuilder, size As Integer,
        filePath As String) As Integer
    End Function

    <DllImport("kernel32.dll", CharSet:=CharSet.Unicode)>
    Private Function WritePrivateProfileString(
        section As String, key As String, value As String,
        filePath As String) As Boolean
    End Function

    Public Function Read(path As String, section As String,
                         key As String, defaultValue As String) As String
        Dim sb As New StringBuilder(512)
        GetPrivateProfileString(section, key, defaultValue,
                                sb, sb.Capacity, path)
        Return sb.ToString()
    End Function

    Public Sub Write(path As String, section As String,
                     key As String, value As String)
        WritePrivateProfileString(section, key, value, path)
    End Sub
End Module

' 使用:
' Dim path As String = Path.Combine(Application.StartupPath, "config.ini")
' IniWin32.Write(path, "App", "Title", "Demo")
' Dim title As String = IniWin32.Read(path, "App", "Title", "")
提示:Unicode 声明配合较新系统上的宽字符路径更稳妥。若必须对接纯 ANSI 旧 INI,可改为 CharSet.Ansi 并统一系统代码页,避免中文乱码。

INF 简述

INF(Setup Information)是 Windows 驱动/组件安装用的说明文件,语法也含节与键值,但面向安装器与设备安装,不是应用日常配置格式。

说明
用途 驱动安装、设备类、复制文件列表等;由安装引擎解释,不是业务程序配置
与 INI 文本外观相似(有 [Section]),语义与工具链不同;勿把 INF 当普通 INI 乱改
本手册范围 应用侧一般不必在 LCode 项目里读写 INF。若只需键值配置,用本页 INI、或 JSON/XML
注意:错误修改系统/驱动 INF 可能导致设备无法安装。本页不对 INF 解析或安装流程提供示例。

注意与易错点

说明
编码 自写助手与对端工具编码须一致;Win32 Ansi/Unicode 声明也要与文件实际编码匹配。
路径权限 写到 Program Files 等受保护目录可能失败;优先用户目录或 Application.StartupPath(若该目录可写)。
并发 多进程同时写同一 INI 可能互相覆盖;需要时加文件锁或改用数据库/单写进程。
自写助手局限 示例会重写整文件,注释与原有格式可能丢失;要严格保真可用 Win32 或成熟第三方库。
C# 语言版本 LCode / .NET 4.8 按 C# 5.0:勿用 $"…"nameof?. 等。

相关阅读