Posted by & filed under programming, windows.


レジストリでなくて、ファイルとして設定を保存したいとき、真っ先に思いついたのが ini ファイルだが、これはもう過去の遺物らしい。

APIも、16bit Windowsアプリケーション用の WIN32API が残されているのみで、使い勝手が悪い。

C++/CLI .net 環境で設定をファイルとして保存したいときは、XML でシリアライズすることで実現できる。(バイナリでシリアライズも可能)

まず、保存する設定群は一つのオブジェクトにまとめる。

AppConfig.h
#pragma once

public ref class PosConfig
{
public:

	PosConfig(void){;}
	System::String^ MySQLhost;
	System::String^ MySQLuser;
	System::String^ MySQLpass;
	System::String^ MySQLdb;
};

public クラスでないと、実行時に保護レベル違反が発生する。
コンパイル時にも、デバッグ時にも何も言ってくれないので注意。

名前空間 “System::Xml::Serialization” にクラスが定義されているので、使う。

XmlConfig.h
#pragma once
#include "PosConfig.h"

using namespace System::IO;
using namespace System::Xml::Serialization;
using namespace System::Runtime::InteropServices;

ref class XmlConfig {
    private: System::String^ configFile;
    public: XmlConfig(System::String^ filename) {
	    this->configFile = filename;
    }

    public: PosConfig^ Load(void) {
	    XmlSerializer^ serializer = gcnew XmlSerializer(PosConfig::typeid);
	    FileStream^ fs = gcnew FileStream(this->configFile, FileMode::Open);
	    PosConfig^ ret = (PosConfig^)serializer->Deserialize(fs);
	    fs->Close();
	    return ret;
    }

    public: System::Void Save(PosConfig^ conf) {
	    XmlSerializer^ serializer = gcnew XmlSerializer(PosConfig::typeid);
	    FileStream^ fs = gcnew FileStream(this->configFile, FileMode::OpenOrCreate);
	    serializer->Serialize(fs, conf);
	    fs->Close();
    }
};

使い方は、こんな感じ。

#include "XmlConfig.h"
・・・
    XmlConfig^ xml = gcnew XmlConfig(System::IO::Directory::GetCurrentDirectory() + "\config.xml");
    AppConfig^ conf = xml->Load();
・・・
・・・
    XmlConfig^ xml = gcnew XmlConfig(System::IO::Directory::GetCurrentDirectory() + "\config.xml");
    AppConfig^ conf = gcnew AppConfig();
    conf->MySQLhost = "localhost";
    conf->MySQLuser = "hoge";
    conf->MySQLpass = "example";
    conf->MySQLdb = "hoge";
    xml->Save(conf);
・・・

ここでちょっと嵌ったのが、 GetCurrentDirectory。
Win32API を利用したいときは、 windows.h を include する必要があるが、そのヘッダの中にこんな定義がある。

WinBase.h
#ifdef UNICODE
#define GetCurrentDirectory  GetCurrentDirectoryW
#else
#define GetCurrentDirectory  GetCurrentDirectoryA
#endif // !UNICODE

この定義のために、コンパイル時に C2039C2660 エラーが発生。スコープ解決演算子を使っているのにダメ。
どうやら、 ” #define > :: ” みたいだ。

ということで、 windows.h を include するときは、その後で #undef してやる。

#include <windows.h>
#undef GetCurrentDirectory

これでOK。