初期化ファイル
プログラムのためのデータ
ユーザーが設定したデータなどは、次回、プログラムを起動した時にも反映するべきです
もちろん、その手段はディスクファイルとして保存する方法が考えられます
この時、どのようなデータ形式を取るかはプログラマしだいと言えます
独自の仕様の構造体を作成し、それをバイナリファイルとして保存することも考えられますし
筆者としては、独自の XML アプリケーションを策定し、XML で保存することを推奨します
もっとも、この場合は、XML のプログラム技術を身につける必要があります
しかし、そんな面倒なことをしなくても Windows はその手段を用意しています
アプリケーションが自分自身のための情報を保存する簡単な方法に
レジストリ と 初期化ファイル *.ini のどちらかを選択することができます
レジストリは、全てのアプリケーションが共有する木構造型のデータ領域で
Windows の重要データが格納されている Windows データベースの中枢です
Microsoft は、アプリケーションがシステムのデータを保存する場合
レジストリに保存することを推奨しています
レジストリについては後記するとして、この場では初期化ファイルに付いて解説します
初期化ファイルは、アプリケーションが独自に保有するファイルと
Windows が統合して保有する WIN.INI ファイルに分けられます
一般に、アプリケーションが独自の設定情報を保存する手段として
もっとも手軽でわかりやすいのが、プライベート初期化ファイル *.INI です
初期化ファイルはセクションで項目を区切り、キーを与えることができます
つまり、セクションとそれに属する複数のキーで表現されるのです
これらは単純なテキストデータとして扱われるため、エディタでの編集も簡単です
[Section1]
KeyID1 = Value1
KeyID2 = Value2
...
[Section2]
...
これが、初期化ファイルの実体です
Section はセクションの名前を指定します
KeyID に保存する値の識別子を指定し、Value が目的の値や文字列となります
開発者は、わざわざ初期化ファイルを読みこんで分析する必要はありません
キーの値を取得するには GetPrivateProfileInt() 関数を使います
UINT GetPrivateProfileInt(
LPCTSTR lpAppName , LPCTSTR lpKeyName ,
INT nDefault , LPCTSTR lpFileName
);
lpAppName は、セクションの名前が格納された文字列のポインタを
lpKeyName は、取得する値のキーの名前を指定します
この関数は、このキーが指す文字列を自動的に数値に変換してくれます
nDefault には、指定したキーが見つからなかった時に返すべきデフォルト値を指定します
lpFileName は読みこむ初期化ファイルの名前を指定します
この関数は、指定したキーの値を、キーが存在しなければデフォルト値を返します
初期化ファイルにキーを保存するには WritePrivateProfileSection() を用います
BOOL WritePrivateProfileSection(
LPCTSTR lpAppName , LPCTSTR lpString ,
LPCTSTR lpFileName
);
lpAppName は、セクション名を示す文字列を指定します
lpString は、セクションに保存する一連のキーと値をあらわす文字列へのポインタです
この文字列は、「キー=値」で一単位となり、それぞれは NULL で区切られます
文字列の最後は連続した二つの NULL で表します
lpFileName には、保存する初期化ファイルの名前を指定します
関数が成功すれば 0 以外、失敗すれば 0 を返します
これらの関数を使えば、簡単に初期化ファイルの値にアクセスすることができます
初期化ファイルを扱う関数で、初期化ファイルの名前を指定する時
特定のディレクトリの初期化ファイルの場合フルパスで指定します
ファイル名だけの場合、この関数は Windows ディレクトリを参照します
#include <windows.h>
#define TITLE TEXT("Kitty on your lap")
LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;
PAINTSTRUCT ps;
static int iCount;
static TCHAR strText[1024] , strPath[MAX_PATH];
switch (msg) {
case WM_DESTROY:
WritePrivateProfileSection(TEXT("HEAD") , strText , strPath);
PostQuitMessage(0);
return 0;
case WM_CREATE:
GetCurrentDirectory(MAX_PATH , strPath);
wsprintf(strPath , TEXT("%s\\%s") , strPath , TEXT("TEST.INI"));
iCount = GetPrivateProfileInt(
TEXT("HEAD") , TEXT("COUNT") , 0 , strPath
);
iCount++;
wsprintf(strText , TEXT("COUNT = %d") , iCount);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd , &ps);
TextOut(hdc , 0 , 0 , strText , lstrlen(strText));
EndPaint(hWnd , &ps);
}
return DefWindowProc(hWnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hWnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hWnd = CreateWindow(
TEXT("KITTY") , TITLE ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hWnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
このプログラムは、次のような初期化ファイルを扱います
[HEAD]
COUNT = 4
プログラムを起動するたびに COUNT キーの値が加算されます
WM_DESTROY メッセージが発行されれば、プログラムは加算した値を保存するため
結果として、プログラムが何回起動されたのかを知ることができます
キーが文字列の値を持っている場合は GetPrivateProfileString() を使います
この関数は、基本的に先ほどと同じですが、キーの値をバッファに保存します
DWORD GetPrivateProfileString(
LPCTSTR lpAppName , LPCTSTR lpKeyName ,
LPCTSTR lpDefault ,
LPTSTR lpReturnedString , DWORD nSize ,
LPCTSTR lpFileName
);
lpAppName はセクションを、lpKeyName はキーを、それぞれ文字列で指定します
lpDefault には指定したキーが見つからなかったときの、デフォルトの文字列を指定します
lpReturnedString には、文字列を受けるバッファへのポインタを、
nSize は、lpReturnedString で指定したバッファのサイズを指定します
lpFileName は、読みこむ初期化ファイルの名前を指定します
関数は、バッファにコピーした文字数を返します
バッファのサイズが足りなければ、文字列は切り捨てられ
nSize 引数で指定したサイズから NULL 文字分を引いた値が返されます
#include <windows.h>
#define TITLE TEXT("Kitty on your lap")
LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;
PAINTSTRUCT ps;
static TCHAR strText[1024] , strPath[MAX_PATH];
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:
GetCurrentDirectory(MAX_PATH , strPath);
wsprintf(strPath , TEXT("%s\\%s") , strPath , TEXT("TEST.INI"));
GetPrivateProfileString(
TEXT("HEAD") , TEXT("USERNAME") , 0 ,
strText , 1024 , strPath
);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd , &ps);
TextOut(hdc , 0 , 0 , strText , lstrlen(strText));
EndPaint(hWnd , &ps);
}
return DefWindowProc(hWnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hWnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hWnd = CreateWindow(
TEXT("KITTY") , TITLE ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hWnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
このプログラムは、HEAD セクションの USERNAME にある文字列を読みこみ
これをウィンドウのクライアント領域に表示するというものです
初期化ファイルは、例えば次のようにあらかじめ設定しておきます
[HEAD]
USERNAME = Leon Akasaka
この初期化ファイルを用いれば LeonAkasaka という文字が表示されるでしょう
GetPrivateProfileString() には、この他にも面白い使い方があります
この関数はセクションやキーの列挙をバッファに格納することも可能です
セクションとキーを指定する、第1、第2引数を NULL にすればセクションを
第2引数だけを NULL にした場合はキーの名前を列挙した文字列をバッファに保存します
セクションやキーは、それぞれ NULL で区切られ
その終端は連続した NULL で終わります
#include <windows.h>
#define TITLE TEXT("Kitty on your lap")
LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;
PAINTSTRUCT ps;
int iCount;
static TCHAR strText[1024] , strPath[MAX_PATH];
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:
GetCurrentDirectory(MAX_PATH , strPath);
wsprintf(strPath , TEXT("%s\\%s") , strPath , TEXT("TEST.INI"));
GetPrivateProfileString(
NULL , NULL , 0 , strText , 1024 , strPath
);
for (iCount = 0 ; ; iCount++) {
if (*(strText + iCount) == 0) {
if (*(strText + iCount + 1) == 0) break;
*(strText + iCount) = '-';
}
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd , &ps);
TextOut(hdc , 0 , 0 , strText , lstrlen(strText));
EndPaint(hWnd , &ps);
}
return DefWindowProc(hWnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {
HWND hWnd;
MSG msg;
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return -1;
hWnd = CreateWindow(
TEXT("KITTY") , TITLE ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hWnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
このプログラムを実行すると、プログラムが置かれているディレクトリ内の
TEST.INI 初期化ファイルのセクションをウィンドウに列挙します
それぞれのセクションは、ハイフン '-' で区切られます
GetPrivateProfileInt()
UINT GetPrivateProfileInt(
LPCTSTR lpAppName , LPCTSTR lpKeyName ,
INT nDefault , LPCTSTR lpFileName
);
プライベート初期化ファイルのキーの数値を返します
lpAppName - セクションの名前を示す文字列を指定します
lpKeyName - 取得する値のキーの名前を指定します
nDefault - キーが見つからなかった時に返すべきデフォルト値を指定します
lpFileName - 読みこむ初期化ファイルの名前を指定します
戻り値 - 指定したキーの値、キーが存在しなければデフォルト値
WriteProfileSection()
BOOL WriteProfileSection(
LPCTSTR lpAppName , LPCTSTR lpString ,
LPCTSTR lpFileName
);
プライベート初期化ファイルにキーと文字列を保存します
lpAppName - セクションの名前を示す文字列を指定します
lpString - セクションに保存する一連のキーと値をあらわす文字列へのポインタです
lpFileName - 保存する初期化ファイルの名前を指定します
戻り値 - 成功すれば 0 以外、失敗すれば 0
GetPrivateProfileString()
DWORD GetPrivateProfileString(
LPCTSTR lpAppName , LPCTSTR lpKeyName ,
LPCTSTR lpDefault ,
LPTSTR lpReturnedString , DWORD nSize ,
LPCTSTR lpFileName
);
プライベート初期化ファイルのキーの文字列を読みこみます
lpAppName - セクションの名前を示す文字列を指定します
lpKeyName - 取得する文字列のキーの名前を指定します
lpDefault - キーが見つからなかったときの、デフォルトの文字列を指定します
lpReturnedString - 文字列を受けるバッファへのポインタを指定します
nSize - lpReturnedString で指定したバッファのサイズを指定します
lpFileName - 読みこむ初期化ファイルの名前を指定します
戻り値 - バッファにコピーした文字数