.NET TIPS

INIファイルを読み書きするには?

株式会社ピーデー 川俣 晶
2003/06/20

 INIファイルは、16bit CPUに対応したWindows 3.1以前のWindowsで使用されていた各種設定の保存用ファイルである。拡張子が「.ini」であることから、INIファイル(イニ・ファイル)と呼ばれる。これは、単純なテキスト・ファイルに格納されることから、扱いやすく、さまざまな用途に使用された。しかし、単純な構造しか持てず、また大量のデータ書き込みなどを行うと遅いことから、32bit時代のWindowsではレジストリにその役割を取って代わられた。現在では、レジストリも過去のものになりつつあり、アプリケーションに関する情報はXMLファイルとして保存することが推奨されている。

 このようにすでに2世代も古い技術ではあるが、過去の資産の継承という意味で、INIファイルを扱うことが求められる場合がある。しかし、.NET FrameworkにはINIファイルを扱う機能は含まれていない。そのため、これを扱う機能を別途用意する必要が生じる。

 INIファイル自身は単純な構造であるため、テキスト・ファイルとして開いて処理することも不可能ではない。しかし、INIファイルの種類によっては、レジストリにマップされたり、OSがキャッシュしていたりする場合もあるので、システムが提供しているWin32 APIを呼び出す方法を使うのがより確実だろう。

 なお、本稿では、INIファイルを扱うWin32 APIに関する基本的な知識を持っていることを前提として説明を行っている。

DLL呼び出しを宣言する

 このTIPSのために、Win32 API呼び出しを行う宣言を作成した。これを用いれば、.NETのアプリケーションからWin32 API呼び出しを簡単に行うことができる。ただし、1つだけ問題に遭遇した。StringBuilderクラス(System.Text名前空間)を利用して文字列を受け取る通常のWin32 API呼び出し手法では、INIファイル中の指定セクションのキー一覧を得る機能と、指定ファイルのセクション一覧を得る機能が実現できなかったのである(文字列を受け取るWin32 API呼び出しについては「TIPS:Win32 APIやDLL関数に文字列や文字列バッファを渡すには?」で解説している)。通常は、Win32 APIにあるGetPrivateProfileString関数を用いて結果の文字列を受け取るが、この2つの機能を使用する場合に限り、返される文字列が複数の文字列から構成される特殊な構造になってしまう。それが、.NET側の型とうまくマーシャリングができなかったのである。これを回避するために、byte配列を使って受け取り、byte配列を明示的に文字列に変換するという回りくどいコーディングが必要とされた。もし、繰り返し使う場合は、この手順もライブラリ化してしまうとよいだろう。

 さて、Win32 API呼び出しを行うための宣言は、以下に示したサンプル・プログラム中のIniFileHandlerクラスに当たる。これはそのままコピーして利用することができる。Win32 APIの宣言さえすれば、あとは通常のメソッド呼び出しと同じようにWin32 APIを呼び出して利用することができる。それぞれの機能がどのようなコードで実現されているかはサンプル・プログラムのコメントで場所を示した。

// inifile.cs

using System;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;

class IniFileHandler {
  [DllImport("KERNEL32.DLL")]
  public static extern uint
    GetPrivateProfileString(string lpAppName,
    string lpKeyName, string lpDefault,
    StringBuilder lpReturnedString, uint nSize,
    string lpFileName);

  [DllImport("KERNEL32.DLL",
      EntryPoint="GetPrivateProfileStringA")]
  public static extern uint
    GetPrivateProfileStringByByteArray(string lpAppName,
    string lpKeyName, string lpDefault,
    byte [] lpReturnedString, uint nSize,
    string lpFileName);

  [DllImport("KERNEL32.DLL")]
  public static extern uint
    GetPrivateProfileInt( string lpAppName,
    string lpKeyName, int nDefault, string lpFileName );

  [DllImport("KERNEL32.DLL")]
  public static extern uint WritePrivateProfileString(
    string lpAppName,
    string lpKeyName,
    string lpString,
    string lpFileName);
}

public class Sample {
  static void Main() {

    // キーと値を書き加える
    IniFileHandler.WritePrivateProfileString(
        "アプリ1", "キー1", "ハロー", @"c:\sample.ini");
    IniFileHandler.WritePrivateProfileString(
        "アプリ1", "キー2", "1234", @"c:\sample.ini");
    IniFileHandler.WritePrivateProfileString(
        "アプリ2", "キー1", "good morning", @"c:\sample.ini");

    // 文字列を読み出す
    StringBuilder sb = new StringBuilder(1024);
   
    IniFileHandler.GetPrivateProfileString("アプリ1", "キー1",
            "default", sb, (uint)sb.Capacity, @"c:\sample.ini");

    Console.WriteLine(
            "アプリ1セクションに含まれるキー1の値: {0}",
            sb.ToString());

    // 整数値を読み出す
    uint resultValue
            = IniFileHandler.GetPrivateProfileInt(
                "アプリ1", "キー2", 0, @"c:\sample.ini");
    Console.WriteLine(
            "アプリ1セクションに含まれるキー2の値: {0}",
            resultValue);

    // 指定セクションのキーの一覧を得る
    byte [] ar1 = new byte[1024];
    uint resultSize1
          = IniFileHandler.GetPrivateProfileStringByByteArray(
                "アプリ1", null, "default", ar1,
                (uint)ar1.Length, @"c:\sample.ini");
    string result1 = Encoding.Default.GetString(
                            ar1, 0, (int)resultSize1-1);
    string [] keys = result1.Split('\0');
    foreach (string key in keys) {
      Console.WriteLine(
        "アプリ1セクションに含まれるキー名: {0}", key);
    }

    // 指定ファイルのセクションの一覧を得る
    byte [] ar2 = new byte[1024];
    uint resultSize2
          = IniFileHandler.GetPrivateProfileStringByByteArray(
                null, null, "default", ar2,
                (uint)ar2.Length, @"c:\sample.ini");
    string result2 = Encoding.Default.GetString(
                            ar2, 0, (int)resultSize2-1);
    string [] sections = result2.Split('\0');
    foreach (string section in sections) {
      Console.WriteLine(
          "このファイルに含まれるセクション名: {0}", section);
    }

    // 1つのキーと値のペアを削除する
    IniFileHandler.WritePrivateProfileString(
        "アプリ2", "キー1", null, @"c:\sample.ini");

    // 指定セクション内の全てのキーと値のペアを削除する
    IniFileHandler.WritePrivateProfileString(
        "アプリ1", null, null, @"c:\sample.ini");
  }
}

// コンパイル方法:csc inifile.cs
INIファイルを読み書きするC#のサンプル・プログラム(inifile.cs)

 ここではINIファイルに対するそれぞれの基本操作について、順に解説していこう。上記のサンプル・プログラムの該当個所と照らし合わせながら読み進めていただきたい。

■キーと値を書き加える

 シンプルにWritePrivateProfileString関数を呼び出せば実現できる。特に使い方に難しいところはない。

■文字列を読み出す

 GetPrivateProfileString関数を呼び出して実現する。その際、結果を得る文字列はstring型ではなく、StringBuilder型であることに注意が必要である。ここで確保された変数内に、文字列が返される。そのため、文字列を受け取るために、1つのStringBuilder型のインスタンスを作成しておく必要がある。

■整数値を読み出す

 これもシンプルにGetPrivateProfileInt関数を呼び出せば実現できる。文字列と違って整数値を受け取る場合は、特に難しいところはない。

■指定セクションのキーの一覧を得る

 GetPrivateProfileString関数を呼び出して実現する。この関数で第2パラメータをnullまたはNothingにすると、指定セクションのキーの一覧が得られる。ここでは結果をbyte配列で受け取るために、別個に宣言したGetPrivateProfileStringByByteArrayメソッドを呼び出している。もちろん、これにより実際に呼び出されるのは、GetPrivateProfileString関数である。

 受け取ったbyte配列は、System.Text.Encoding.Default.GetStringメソッドで文字列に変換することができる。文字列は文字コード“0”の文字('\0')で区切られているので、stringクラスのSplitメソッドで分解することができる。その際、最後が文字コード“0”の文字であると、結果となる配列の最後に空文字列が生成されてしまう。これを回避するために、ここでは最後の文字を切り落とすべく、GetStringメソッド呼び出し時に、1つだけ短いバイト数を指定している(「resultSize1 ? 1」の部分)。

■指定ファイルのセクションの一覧を得る

 GetPrivateProfileString関数を呼び出して実現する。この関数で第1パラメータをnullまたはNothingにすると、指定ファイルのセクションの一覧が得られる。ここでも結果をbyte配列で受け取るために宣言したGetPrivateProfileStringByByteArrayメソッドを呼び出している。そのほかの処理方法は、指定セクションのキーの一覧を得る場合と同様である。

■1つのキーと値のペアを削除する

 WritePrivateProfileString関数を呼び出せば実現できる。第3パラメータにnullまたはNothingを指定すると、指定キーに対応するキーと値のペアは取り除かれる。

■指定セクション内のすべてのキーと値のペアを削除する

 WritePrivateProfileString関数を呼び出せば実現できる。第2パラメータにnullまたはNothingを指定すると、指定セクション内のすべてのキーと値のペアは削除される。そのセクション自身も削除される。

 余談だが、今回のサンプル・プログラムはANSI版のWin32 APIを利用することを前提として組んである。これは、日本では通常INIファイルがシフトJISで保存されていることによるものである。つまり、シフトJISで表現できない文字をINIファイルで扱う状況は通常発生しない、という前提による。しかし、もしUnicode対応が必要だとしても、変更することは容易である。必要に応じて書き換えて利用していただきたい。End of Article

カテゴリ:クラス・ライブラリ 処理対象:INIファイル
使用ライブラリ:StringBuilderクラス(System.Text名前空間)
使用ライブラリ:DllImport属性(System.Runtime.InteropServices名前空間)
関連TIPS:Win32 APIやDLL関数に文字列や文字列バッファを渡すには?
 
この記事と関連性の高い別の.NET TIPS
Win32 APIやDLL関数を呼び出すには?
Win32 APIやDLL関数に文字列や文字列バッファを渡すには?
実行ファイルからアプリケーションのアイコンを取得するには?
ドライブをフォーマットするためのダイアログを表示するには?
長いパスを8.3形式の短いパスに変換するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

Insider.NET 記事ランキング

本日 月間
ソリューションFLASH