13 April 2008

(Windows Mobile) C#で簡易的なINIファイル読み書き

パソコン版のC#なら、無理やりWin32SDK (kernel32.dll) の GetPrivateProfileString などのシステムコールを呼び出せばいいのだが…
Windows Mobile SDK には、当然のことながら Windows95のレガシーAPIは含まれるはずも無い。

で、デバイスを汚さずに(レジストリを使うなんてご法度)、エクスプローラで簡単にアンインストールできると言えば、INIファイルしか存在しない。 (.NET Frameworkの推奨は XMLファイルなのだが、eVC++でも簡単に使うとなると少し難しい)

今回のキーワードは

プログラム自身の実行フォルダに、INIファイルを作成する
INIファイル構造は、簡易的なものとする (アプリケーション名のセクション分けは使わない)

まず、プログラム自身のフォルダを得る。 これが結構難しい。
無理やりSDKを呼び出すならば…
WINAPI DWORD GetModuleFileName(HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
が使えそう。
パソコン版の .NET Framework ならば
Application.StartupPath
が使えるが、.NET Compact Framework には存在しない。

Googleで検索すると、代替のヒントが得られた。

System.Reflection.Assembly.GetExecutingAssembly().GetModules()
を用いるとプロセス自身の実行ファイル関連の情報が得られるらしい。

ということで、

INIファイルのパスを作成する部分

public string GetIniPath()
{
string s;

// パス名
s = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
s += "\\"; // ルートフォルダの場合、""が返されるので、常にパスを完成させるために "\\"を付加
// 拡張子無しのファイル名
s += System.IO.Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
// 拡張子付加
s += ".ini";
return s;
}

インターフェース部分

INIファイル読み書きの部分

// 読み込み
public string GetPrivateProfileStringNet(string sKey, string sIniPath)
{
string sTmp;
string [] sTmpProfile;
int nFound = 0;

System.IO.StreamReader sr = new System.IO.StreamReader(sIniPath, System.Text.Encoding.GetEncoding(932));

for(;;)
{
sTmp = sr.ReadLine();
if(sTmp == null) break;
if(sTmp == "") continue;
sTmpProfile = ProfileStringParse(sTmp);
if(sTmpProfile[0] == sKey)
{
sTmp = sTmpProfile[1];
nFound = 1;
break;
}
}
sr.Close();

if(nFound == 1) return sTmp;
else return "";
}

// INIファイル書き込み
public int WritePrivateProfileStringNet(string sKey, string sData, string sIniPath)
{
if(System.IO.File.Exists(sIniPath))
{ // INIファイルが存在する場合

string sTmp;
string [] sTmpProfile;
int nWritten = 0;

// バックアップファイルの作成
string sBkupPath = sIniPath + ".bak";
System.IO.File.Copy(sIniPath, sBkupPath, true);

System.IO.StreamReader sr = new System.IO.StreamReader(sBkupPath, System.Text.Encoding.GetEncoding(932));
System.IO.StreamWriter sw = new System.IO.StreamWriter(sIniPath, false, System.Text.Encoding.GetEncoding(932));

for(;;)
{
sTmp = sr.ReadLine();
if(sTmp == null) break;
if(sTmp == "") continue;
sTmpProfile = ProfileStringParse(sTmp);
if(sTmpProfile[0] == sKey)
{
sw.WriteLine("{0}={1}", sKey, sData);
nWritten = 1;
}
else
{
sw.WriteLine(sTmp);
}
}
if(nWritten != 1) sw.WriteLine("{0}={1}", sKey, sData);

sw.Close();
sr.Close();
}
else
{ // INIファイルが存在しない場合
System.IO.StreamWriter sw = new System.IO.StreamWriter(sIniPath, false, System.Text.Encoding.GetEncoding(932));
sw.WriteLine("{0}={1}", sKey, sData);
sw.Close();
}

return 1;
}

// "="の前後で分ける
public string [] ProfileStringParse(string sLine)
{
string [] sReturn = sLine.Split(new Char [] {'='});

return sReturn;
}

ファイル入出力のエラーチェックは全くしていないプロトタイプ版ですので、このまま用いないように。