2011年9月6日火曜日

設定の保存にxmlファイルを使う(C#)

C#で設定ファイルを簡単に実装しようと思ったらXmlSerializerを利用して設定用クラスの情報をxmlファイルにダンプ、ロードする方法が楽だ。
// 設定用クラス
public Config{
// publicにする必要あり
public string config1 = "初期値";
}

// 起動後
// 設定ファイル読み込み
// Configの部分は設定クラス名で変わる
XmlSerializer serializer = new XmlSerializer(typeof(Config));
string confFile = Path.Combine(Application.StartupPath, Path.GetFileNameWithoutExtension(Application.ExecutablePath) + ".xml");
if (File.Exists(confFile))
{
using (FileStream fs = new FileStream(confFile, FileMode.Open))
{
Config = (Config)serializer.Deserialize(fs);
}
}
// ファイルがない場合は初期値をロード
else
{
Config = new Config();
}

// いろいろな処理をやる。

// 終了前
// 設定ファイルの保存
using (FileStream fs = new FileStream(confFile, FileMode.Create))
{
serializer.Serialize(fs, Config);
}
これだけで動作はするのだが、環境によっては処理がハングすることがあるらしい。標準のXmlSerializerでは実行時に動的コンパイルでシリアライズ処理用のバイナリを作るが、この処理がまずいみたいなので、事前にコンパイル済みのバイナリを用意してそれを利用するようにすれば良いらしい。手順をは以下の通り。
  1. 設定用クラスを含めてコンパイルしたアセンブリからシリアライズ用のDLLを作成する。具体的にはビルドイベントでビルド後のイベントに「"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\sgen.exe" /f "$(TargetPath)" /o:"$(ProjectDir)"」を追加する。
  2. 作成されたDLL(アセンブリ名.XmlSerializers.dll)を参照設定に追加する。
  3. DLLを利用してシリアライズするようにプログラムを修正する。
using Microsoft.Xml.Serialization.GeneratedAssembly;  //先頭に追加

// ConfigSerializerの部分は、設定用のクラス名によって変わる。
// XmlSerializer serializer = new XmlSerializer(typeof(Config));
ConfigSerializer serializer = new ConfigSerializer();
1つだけ自分がハマったことがあったので注意として付記しておく。DLLを参照に追加するとデバック時にDLLが見つからずにエラーにならないよう、ビルドのたびにアセンブリの出力ディレクトリに参照DLLをコピーしてくれるローカルコピーという機能がある。

この際、親切なことに「参照しているDLL名.xml」(&.pdb)という名前のファイルがあればそれもコピーしてくれる。それだけなら歓迎なのだが、何故か「アセンブリ名.xml」のファイルがあれば、一緒にコピーしてしまうという謎の仕様になっているらしい。

何がまずいかというと、このサンプルのように設定ファイル名を「アセンブリ名.xml」にしていて、sgenに/oオプションを付けずアセンブリと同じディレクトリにDLLを出力、それをそのまま参照に追加するという条件が重なると困った現象が起きる。

例えばReleaseディレクトリに出力したDLLを参照に追加したとする。Releaseビルドで一回でもデバッグを実行して設定ファイルを出力した状態で、Debugビルドに切り替えてデバッグを行うと、設定ファイルがReleaseディレクトリからDebugディレクトリにコピーされてしまい、Debugビルドでアプリが書き換えた設定が再ビルドのたびにリセットされてしまうという… これで何時間無駄にしたことか…

対策は以下のどれか
  • DubugビルドとReleseビルドで参照設定の指定をちゃんと切り替える。GUIからは無理なのでプロジェクトファイルをテキストエディタで直接編集する必要あり。
  • ローカルコピーを無効にする。参照設定のプロパティから切り替えられる。
  • 「アセンブリ名.xml」ファイルが出力されない場所にDLLを出力して、参照設定に追加する。
今回の例では3番目を採用した。

0 件のコメント:

コメントを投稿