iniファイルの読み書き用クラス

# はじめに

アプリケーションの設定値 (フォームの位置やサイズなど) を保存し, 次回起動時にこの設定値で起動する、ということを実施することがあると思います. この設定値を保存する形式は, .NET Frameworkアプリケーションではxml形式が推奨されていますが, 未だにini形式が好まれたりします.
そのため, iniファイルの読み書き用のクラスを作成してみました.

# NativeMethods

使用するWin32 APIの定義はNativeMethodsクラスに以下のように定義します.

```csharp:NativeMethods.cs
using System.Text;
using System.Runtime.InteropServices;

internal sealed class NativeMethods
{

    private NativeMethods() { }

    [DllImport("kernel32", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, ExactSpelling = false)]
    internal static extern uint GetPrivateProfileString(
       [MarshalAs(UnmanagedType.LPWStr), In] string lpAppName,
       [MarshalAs(UnmanagedType.LPWStr), In] string lpKeyName,
       [MarshalAs(UnmanagedType.LPWStr), In] string lpDefault,
       [MarshalAs(UnmanagedType.LPWStr), Out] StringBuilder lpReturnString,
       uint nSize,
       [MarshalAs(UnmanagedType.LPWStr), In] string iniFilename);

    [DllImport("kernel32", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
    internal static extern int WritePrivateProfileString(
        [MarshalAs(UnmanagedType.LPWStr), In] string lpAppName,
        [MarshalAs(UnmanagedType.LPWStr), In] string lpKeyName,
        [MarshalAs(UnmanagedType.LPWStr), In] string lpString,
        [MarshalAs(UnmanagedType.LPWStr), In] string lpFileName);

}
```

# Iniクラス

iniファイル読み書き用のクラスは次の通りです.

```csharp:Ini
using System;
using System.ComponentModel;
using System.IO;
using System.Text;

public static class Ini
{

    /// <summary>
    /// INIファイルからキーの値を取得します
    /// <para>戻り値は, 取得が成功したかどうかを示します</para>
    /// </summary>
    /// <typeparam name="T">データ取得する型</typeparam>
    /// <param name="filePath">ファイルパス</param>
    /// <param name="sectionName">セクション名</param>
    /// <param name="keyName">キー名</param>
    /// <param name="defaultValue">初期値</param>
    /// <param name="outputValue">出力値</param>
    /// <returns>取得の成功有無</returns>
    public static bool TryGetValueOrDefault<T>(string filePath, string sectionName, string keyName, T defaultValue, out T outputValue)
    {

        outputValue = defaultValue;

        if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
            return false;

        var sb = new StringBuilder(1024);
        var ret = NativeMethods.GetPrivateProfileString(sectionName, keyName, string.Empty, sb, Convert.ToUInt32(sb.Capacity), filePath);
        if (ret == 0 || string.IsNullOrEmpty(sb.ToString()))
            return false;

        var conv = TypeDescriptor.GetConverter(typeof(T));
        if (conv == null)
            return false;

        try
        {
            outputValue = (T)conv.ConvertFromString(sb.ToString());
        }
        catch (NotSupportedException)
        {
            return false;
        }
        catch (FormatException)
        {
            return false;
        }

        return true;

    }

    /// <summary>
    /// INIファイルからキーの値を取得します
    /// </summary>
    /// <typeparam name="T">データ取得する型</typeparam>
    /// <param name="filePath">ファイルパス</param>
    /// <param name="sectionName">セクション名</param>
    /// <param name="keyName">キー名</param>
    /// <param name="defaultValue">初期値</param>
    /// <returns>キー値</returns>
    public static T GetValueOrDefault<T>(string filePath, string sectionName, string keyName, T defaultValue)
    {
        T ret = defaultValue;
        TryGetValueOrDefault(filePath, sectionName, keyName, defaultValue, out ret);
        return ret;
    }

    /// <summary>
    /// INIファイルにデータを書き込みます
    /// </summary>
    /// <param name="filePath">ファイルパス</param>
    /// <param name="sectionName">セクション名</param>
    /// <param name="keyName">キー名</param>
    /// <param name="outputValue">出力値</param>
    public static void SetValue(string filePath, string sectionName, string keyName, string outputValue) =>
        NativeMethods.WritePrivateProfileString(sectionName, keyName, outputValue, filePath);


}
```

# 使い方

WinFormsアプリケーションでフォームの位置・サイズをiniファイルへの読み書きするサンプルです.
アプリケーションと同一ディレクトリーにTest.iniが存在しているとし, このTest.iniに設定値を読み書きします. FormのLoadイベントでiniファイルの内容を読み込み, FormClosingイベントでiniファイルに書き込みしています.

```csharp:サンプルアプリ
private void MainForm_Load(object sender, EventArgs e)
{
    StartPosition = FormStartPosition.Manual;

    //iniから前回のフォーム位置、サイズ取得
    var filePath = System.IO.Path.Combine(Environment.CurrentDirectory, "Test.ini");
    Left = Ini.GetValueOrDefault(filePath, Name, nameof(Left), 0);
    Top = Ini.GetValueOrDefault(filePath, Name, nameof(Top), 0);
    Width = Ini.GetValueOrDefault(filePath, Name, nameof(Width), 100);
    Height = Ini.GetValueOrDefault(filePath, Name, nameof(Height), 100);
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    //iniにフォーム位置、サイズ保存
    var filePath = System.IO.Path.Combine(Environment.CurrentDirectory, "Test.ini");
    Ini.SetValue(filePath, Name, nameof(Left), Left.ToString());
    Ini.SetValue(filePath, Name, nameof(Top), Top.ToString());
    Ini.SetValue(filePath, Name, nameof(Width), Width.ToString());
    Ini.SetValue(filePath, Name, nameof(Height), Height.ToString());
}
```

# 最後に
今回作成したIniクラスを使用することで簡易にiniファイルの内容を読み書きできるようになりました.