Version 5.34
閉じる前にレジストリに
「さて最後の仕上げ、今回はレジストリへの保存をしてみましょう」
『レジストリって前に使ったね。 Ver 3.19 ( No.044 ) とかから3回くら
いずっとレジストリ』
「軽く見返してみて」
『えーっと、データをレジストリに保存するんだよね。 CWinApp の関数、
SetRegistryKey() とか GetProfileInt() を使うんだね』
「そう、この辺のメンバ関数でレジストリに読み書きできます」
『ふと思ったんだけど、レジストリの操作も API でできるの?』
「もちろんできるよ。 CWinApp のメンバ関数を使った場合って、レジスト
リの【HKEY_CURRENT_USER\Software\KamichanFactory】に書き込まれるで
しょ。でも実際にはレジストリのどの場所にも書き込めなきゃいけないわけ
で」
『 API を使うとそれが自由にできるんだ! ってことは……』
「そう、使うの難しいんだよね」
『やっぱし』
「レジストリ関係の API は Reg なんたらって関数名だから」
『 RegCreateKeyEx() とかあるね』
「気が向いたら使ってみて。実際、当分の間は使う必要ないものだから今は
パスするけど」
『引数多くて難しそう……で、まずどうすればいいんだっけ』
「まずは CWinApp::SetRegistryKey() 」
『そうそう、【HKEY_CURRENT_USER\Software\ SetRegistryKey() の引数\ア
プリ名】ってゆーのが作られるんだよねこれで』
「 CWinApp::SetRegistryKey() には前と同じ KamichanFactory でいいかな」
『うん、前のと違うと分かりにくいもん。……ってことは、後から変えるの
って結構めんどい?』
「そういう事になるね。会社名っていうかブランド名は、そう簡単に変えな
い方がいいってことかも」
『そしたら WriteProfileInt() で書き込むんだよね。あれ? Int って整数
値の int だよね。コンボボックスに入ってるのは文字列だから……』
「そう、これじゃ書き込めないんです。でも」
『別のがあるんだ! CWinApp のメンバ関数一覧、 WriteProfileString()
っていうのがある!』
「そう、これで文字列を書き込めます」
『引数は WriteProfileInt() とほとんど同じね。でも……どの引数がどん
なのだっけ』
「じゃ、実際に試してみようか。まず CWinApp::SetRegistryKey() 。これ
は CFileTestApp::InitInstance() で呼び出します」
BOOL CFileTestApp::InitInstance()
{
SetRegistryKey( "KamichanFactory" );
// 以下略。
『ん? ここじゃなきゃダメなの?』
「少なくとも CFileTestApp のメンバ関数の中じゃないとダメ。それ以外の
クラスからは呼べないようになってます。なぜかっていうのはもすこし先に
なってから。 MFC の仕様ってとこかな」
『ふーん。で、 WriteProfileString() の方はどうしよう』
「とりあえずテストだし、 CFileTestDlg::OnBtnBrowse() でやってみよう
か」
void CFileTestDlg::OnBtnBrowse()
{
CWinApp *pcWinApp
= AfxGetApp();
pcWinApp->WriteProfileString( "A", "B", "C" );
// 以下略。
『あれ、ちょっと難しくない?』
「ちょっとね。たとえば、 CComboBox::AddString() を呼び出すときには、
m_cFileCmbBox.GetWindowText( chPath, MAX_PATH - 1 ); って感じに
【変数.メンバ関数】って形式だったでしょ」
『そっか、 WriteProfileString() もそういうふうにしなきゃいけないって
ことだね』
「ここでは【変数】がポインタなんで、 . じゃなく -> を使います」
『……それ教えてもらったっけ?』
「……教えてなかったっけ?」
『……』
「……まーいーや。機能的には . も -> も同じ。左側の変数がポインタな
ら -> を使うってだけの話だから」
『ほい。で、そのポインタを AfxGetApp() で取得っと。あ、この関数は何
度も使ってるね』
「これは MFC の関数で…… FileTest.cpp の中に CFileTestApp theApp;
って変数があるでしょ。これのアドレスを返すのがこの関数」
『ほー。で、それを通して CWinApp::WriteProfileString() を呼ぶわけ
ね。あれ、でも SetRegistryKey() はそうしてないじゃん』
「これは Ver 5.26 ( No.091 ) の SetWindowText() の例と同じ。
cShowBtn.SetWindowText() って呼んでるのと直接 SetWindowText() って呼
んでるのと2種類あるでしょ」
『これと同じパターン?』
「そう。 cShowBtn.SetWindowText() は、直前に作ってる CButton 型変数
cShowBtn を通して呼んでます。直接 SetWindowText() 呼んでる方は、
CFileTestDlg::OnBtnShow() の中で呼んでるんでその CFileTestDlg 型変数
を通して呼んでます」
『へへん憶えてるよん、 CFileTestApp::InitInstance() の中で
CFileTestDlg dlg; ってしてるこの dlg がその変数なんだよね』
「そう! この辺は Ver 3.21 ( No.046 ) とかで散々やってるから憶えて
るか」
『 ってことは、 CFileTestApp::InitInstance() もそういう変数が……そ
うだ! それが theApp だって話だったんだ!』
「 Ver 3.22 ( No.047 ) でやったね。つまり SetRegistryKey() も
WriteProfileString() も同じ theApp を通して呼ばれてるってこと」
『どっちも同じってことね。で、なんだっけ』
「で、 CWinApp::WriteProfileString() がどう動作するのか確認するんで
しょ」
『そうそうそう。ビルドして実行! レジストリ見ると……
【HKEY_CURRENT_USER\Software\KamichanFactory\FILETEST\A】に【名前】
が B 、【データ】が C のができてる!』
「 A が第1引数、 B が第2引数、 C が第3引数だね」
『これで WriteProfileString() は問題なし!』
「さて次の問題。レジストリには、どの段階で書き込めばいいでしょうか」
『え? そうね、アプリが閉じるときとか。前にレジストリに書き込んだと
きは、 CFileTestApp::InitInstance() の中でダイアログ閉じてからだった
よね』
「実は、今回はそれはできないんです。ダイアログが閉じた後、ってことは
もうダイアログそのものが存在しないわけで、もちろんコンボボックスも」
『跡形もなく消えてる! ってことはそれが消える前、かつダイアログが閉
じる時に保存しなきゃいけないんだ。……んなことできるの?』
「もちろん。一番必要ってくらいだからね。ウィンドウが閉じる直前には、
WM_DESTROY っていうメッセージが送られてきます」
『そっか、それに対応するメンバ関数作ればいいんだね。えーっと、クラス
ウィザードで、クラス名を CFileTestDlg にして、メッセージから……
WM_DESTROY みっけ』
「そのメンバ関数を作って」
『 CFileTestDlg::OnDestroy() ができた! とりあえずさっき
CFileTestDlg::OnBtnBrowse() でテストした WriteProfileString のをこっ
ちに移してっと』
「 WriteProfileString() の第1、2引数はそのままにして、第3引数をコ
ンボボックスの1番目のデータ、にしてテストしてみようか」
『ほい。えーっと、そーゆーメンバ関数はーっと、 CComboBox::GetLBText()
がそれっぽい』
「んじゃ使ってみて」
『第1引数はインデックスナンバーだから 0 っと。第2引数は……2種類
あるね』
「 LPTSTR のと CString の参照のとだね。ちょっと CWnd::GetWindowText()
と見比べてみて」
『あ、こっちも CString の参照のがある。でも LPTSTR の方は、最大文字
数も受け取るね』
「ポインタ教えたときに言ったと思うけど、配列のサイズはポインタからは
判らないから」
『 sizeof だとポインタのサイズが返ってくるんだよね、だから最大文字数
を渡す、と』
「はみ出ないようにね。参照は憶えてる?」
『ポインタみたいのだよね。アドレス渡してるのと同じだから、ホントの変
数は関数呼びだしてる側にあって、その変数が関数から操作される、って
ゆーの』
「参照として渡せば、 CString はサイズを自由に変えられるから」
『サイズのこと気にしなくていいんだ! ってことは CString の方が楽っ
てことね。そいや、なんで CComboBox::GetLBText() の LPTSTR 版、なんで
最大サイズ渡さないでいいの?』
「 CComboBox::GetLBTextLen() ってメンバ関数があるから。これで文字列
のサイズが判るから」
『入りきるかどうかそれで調べてからってことね。めんどくさ……』
「それじゃ CString 使った方で試してみて」
『 CString 型変数作って、参照だからそのまま渡せばいいんだから……あ、
CString って LPCTSTR にそのまま渡せるんだよね。だから……』
void CFileTestDlg::OnDestroy()
{
CString cDataStr;
m_cFileCmbBox.GetLBText( 0, cDataStr );
CWinApp *pcWinApp
= AfxGetApp();
pcWinApp->WriteProfileString( "A", "B", cDataStr );
CDialog::OnDestroy();
}
『ビルドして実行、【参照】で選んで【表示】させてから【 OK 】で閉じて
と。んでレジストリエディタで最新表示……おおっ、今選んだファイルのパ
スがちゃんと出てる』
「じゃ、今度はリストのデータすべてをレジストリに書き込んでみましょ
う」
『んーとねー、 "B" のとこを "1" とか "2" とかにして、 cDataStr をリ
ストの各行にして書き込めばいいんじゃないかな』
「じゃ、やってみましょう!」
『…………』
「ひとつずつひとつずつ。まずリスト全部を TRACE() で出力してみるとか」
『そうね、えっと、この前使った CComboBox::GetCount() でリストにいく
つ入ってるのか分かるんだから、これで for 使えばいいんじゃないかな』
void CFileTestDlg::OnDestroy()
{
CString cDataStr;
for( int iNo = 0; iNo < m_cFileCmbBox.GetCount(); iNo++ )
{
m_cFileCmbBox.GetLBText( iNo, cDataStr );
TRACE( "%s\n", cDataStr );
}
CDialog::OnDestroy();
}
『 cDataStr は for の外で作った方がいいんだよね、そうしないと毎回作
り直されちゃうから』
「そ、 Ver 5.21 ( No.086 ) でやったね」
『 for は前になんかで使ったのの使い回しだけど、大丈夫だよね。あのさ、
こうすると m_cFileCmbBox.GetCount() 何度も呼んじゃうけど、ホントは
1回でいいんだよね』
「そうだね。 for の前で const int に入れてそれを使った方がいいかな」
『あ、そっか。で、いつも使ってる CComboBox::GetLBText() で1行拾って
出力、これでいい?』
「うん、大丈夫でしょ。というわけで続く!」
/*
Preview Next Story!
*/
『次回で第100回っすよ水希ちゃん!!』
「というわけで次回はまとめとこれからの方針なんかも」
『質問! あたしがこれまでに憶えた量ってどのくらい?』
「量としてはかなりだね。いっぱしのプログラマーかも」
『おお!』
「でも僕が教えたい量の3分の1にも満たないかも」
『なんですと!!』
「というわけで次回」
< Version 5.35 レジストリにいっぱい書き込もう! >
『につづく!』
「……もしかして欲張りすぎ?」
『そんなにいっぱい憶えらんないよ〜』