→トップへ戻る  →C# Tips

XmlSerializer を使おう。

XmlSerializerとは.NET Frameworkに含まれるクラスライブラリの一つです。 長い名前で言うとSystem.Xml.Serialization.XmlSerializerとなります。 使用するときはSystem.Xmlを参照設定に加える必要があります。

XmlSerializerにはいろんな機能があるのですが、 ここではユーザー設定保存ファイルを簡単に作成するための方法を書きたいと思います。

  1. ユーザー設定保存ファイル(.iniファイル)の代わりに使う。
  2. 保存と読み込み
  3. csc.exeが原因で起動に失敗する問題
  4. Sgen.exeとXmlSerializerのプリコンパイル

ユーザー設定保存ファイル(.iniファイル)の代わりに使う。

C#+.NETではプログラムの情報を保存しておくには次のような方法があります。

  1. iniファイルに書き込む。(扱うためのクラスは用意されていない)
  2. xmlファイルに書き込む。(XmlSerializerクラス)
  3. レジストリに書き込む。(RegistryKeyクラス)
  4. ApplicationSettingsBaseを利用する。

どれも使用する分にはたいして差はないのですが、使わなくなったときの後始末の差から 1,2はファイルを解凍するだけでインストールできるプログラム向き、 3,4はアンインストーラー持ちプログラム向きだと思います。

昔は設定ファイルといえば1のiniファイルだったのですが、.NETには読み書きするためのクラスが用意されていません。 よって自分で作るか(すでに作って公開されている方がいるので利用させてもらうのもいいでしょう。)、 WindowsAPIを使うことになります。

これから説明するXmlSerializerは非常に保存、読み出しが簡単なので、 特に理由がなければiniファイルではなく、xmlファイルに移行することをオススメします。

保存と読み込み

設定情報を一つのクラスにまとめます。 配列や構造体が混じってもかまいません。 ここに初期値が書かれていても、後で読み込みしたときに保存値で上書きされるので大丈夫です。

public class SampleClass
{
    public int Info1 = 10;
    public int[] ColumnsWidth = new int[] { "300", "600", "900" };
    public Size WindowSize;  //後で代入してください
}

たったこれだけのコードで保存できます。 実行ファイルのある場所にtest.xmlとして保存しています。

//XmlSerializerを呼び出す
XmlSerializer serializer = new XmlSerializer(typeof(SampleClass));
//ファイルを作る
FileStream fs = new FileStream(Application.StartupPath + "\\test.xml", FileMode.Create);
//書き込み
serializer.Serialize(fs, scls);  //sclsはSampleClassのインスタンス名
//ファイルを閉じる
fs.Close();

保存内容はこんな感じになります。

<?xml version="1.0"?>
<SampleClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Info1>10</Info1>
  <ColumnsWidth>
    <int>300</int>
    <int>600</int>
    <int>900</int>
  </ColumnsWidth>
  <WindowSize>
    <Width>640</Width>
    <Height>480</Height>
  </WindowSize>
</SampleClass>

読み込みも簡単です。

//設定ファイルがあるときのみ読み込みします。
if (File.Exists(Application.StartupPath + "\\test.xml"))
{
    //XmlSerializerを呼び出す
    XmlSerializer serializer = new XmlSerializer(typeof(SampleClass));
    //ファイルを開く
    FileStream fs = new FileStream(Application.StartupPath + "\\test.xml", FileMode.Open);
    //読み込み
    SampleClass scls = (SampleClass)serializer.Deserialize(fs);
    //ファイルを閉じる
    fs.Close();
}

csc.exeが原因で起動に失敗する問題

こんなに便利なXmlSerializerなんですが、たった一つだけ問題があります。 しかも、それがかなり致命的なんです。あんまり使う人を見かけないのはその所為じゃないかと思います。

[XmlSerializer クラスから起動された csc.exe が一定時間応答を停止する場合がある]
http://support.microsoft.com/kb/899153/ja

必ず起こるわけではなく、たまにしか起こらないのが曲者です。 プログラムを実行したのにもかかわらず起動せず、しばらくすると 「System.Runtime.InteropServices.ExternalException: プログラムの実行待機中にタイムアウトしました。実行されていたコマンドは "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe"(以下省略)」 のようなエラーメッセージが出ます。

この問題は未だに直っていません。 .NET1.1で発覚した問題が直ってないところを見ると、直さないことにしたのでしょう。 問題があるのはWindowsXP側らしいので直しづらいのかもしれません。よって、実装側で回避するしかありません。

回避するにはXmlSerializerのプリコンパイルを行い、実行時にXmlSerializer絡みでcsc.exeを使わないようにします。

Sgen.exeとXmlSerializerのプリコンパイル

この問題を解決するため?に、.Net2.0にはXmlSerializer専用のコンパイラが追加されました。 それがSgen.exeです。

まずはSgen.exeを探しましょう。特に変更していなければ インストールフォルダ\Microsoft Visual Studio 8\SDK\v2.0\Bin\以下にあると思います。

プロジェクトのプロパティを開き、ビルドイベント→ビルド後に実行するコマンドラインで
"インストールフォルダ\Microsoft Visual Studio 8\SDK\v2.0\Bin\sgen.exe" /force "$(TargetPath)"
と書きます。実行条件は「ビルドが成功したとき」でいいでしょう。

ビルドに成功すると、実行ファイルと同じ場所に実行ファイル名.XmlSerializers.dllが得られます。 あとはこれらを常に一緒に置いておけばOKです。