第129章 スクリーンセーバー その3


今回は、スクリーンセーバーの設定情報をINIファイルではなく レジストリに保存する方法を考えてみます。

ご存知のようにレジストリが壊れると悲惨なことになりますので 念には念をいれて、バックアップをとってから始めてください。


さて、前回までのプログラムで保存する情報は、「表示する文字列」 と「スピード」でした。実際にこの情報を格納したレジストリを次に示します。



HKEY_CURRENT_USERキーの下にSoftwareというサブキーがあります。 この下に通常は「会社名」「アプリケーション名」「いろいろな設定」 というような感じでサブキーを作ってデータを保存します。

左の例ではSoftwareの下に「Kumei」というサブキー(会社名または個人名)があり その下にソフトの名前を示す「saver03」というサブキーがあります。 右のウィンドウを見ると「SPEED」と「TEXT」という「名前」があり その横に「300」とか「"粂井康孝 制作"」というデータが保存されているのが わかります。 なお、このような構成をHKEY_CURRENT_USERに作ると 「HKEY_USERS」「.Default」「Software」の下にも全く同じものが 自動的に作られます。

LONG RegCreateKeyEx( HKEY hKey, // 親のキー LPCTSTR lpSubKey, // サブキーの名前 DWORD Reserved, // 予約済み(0にする) LPTSTR lpClass, // キーのクラス名 DWORD dwOptions, // キーの記憶オプション REGSAM samDesired, // キーアクセスのオプション LPSECURITY_ATTRIBUTES lpSecurityAttributes, //セキュリティ属性(NULLの時デフォルト) PHKEY phkResult, // 新しいサブキーを受け取るバッファのポインタ LPDWORD lpdwDisposition // 処理後の結果 );

これで、新しいサブキーを作ったりオープンしたりします。

今回はHKEY_CURRENT_USERの下のサブキーを作るのでこれがhKeyとなります。

また、サブキーの名前は"Software\\Kumei\\saver03"というように ディレクトリを指定するのと同じ方法です。(「\」記号は2つずつ つけます。そうしないと\saver03があると「\s」というエスケープシーケンスと 区別が付かなくなるからです。この手の話はC言語の参考書の最初の方に出ています)

Reservedは0にしておきます。

lpClassはキーのクラス名を指定します。(""を指定)

dwOptionsには、REG_OPTION_NON_VOLATILE かREG_OPTION_VOLATILE を 指定します。前者はデータをディスクに書き込みます。後者は書き込みません。

samDesiredには、キーアクセスオプションです。通常はKEY_ALL_ACCESSです。 詳細はヘルプを見てください。

lpSecurityAttributesにはセキュリティ属性を指定します。特別なことをしないときは NULLです。

phkResultは新しいサブキーを受け取るバッファを指定します。

lpdwDispositionには処理後の結果が入ります。サブキーが作成されたときは REG_CREATED_NEW_KEYが入ります。既存のキーがオープンされたときは REG_OPEND_EXISTING_KEYとなります。

戻り値は成功するとERROR_SUCCESSが返されます。失敗したときはエラーコードが 返されます。

LONG RegSetValueEx( HKEY hKey, // キーのハンドル LPCTSTR lpValueName, // 設定する値の名前 DWORD Reserved, // 予約済み(0にしておく) DWORD dwType, // データのタイプ CONST BYTE *lpData, // 値に保存するデータへのポインタ DWORD cbData // データの大きさ );

hKeyにはRegCreateKeyExで取得したキーを指定します。

lpValueNameには設定する値の名前です。

dwTypeにはデータタイプを指定します。 REG_DWORDは32ビット値、REG_SZは文字列などを指定します。

lpDataは値に保存するデータへのポインタです。CONST BYTE *なので 実際に使うときは型キャストなどに注意してください。

cbDataにはデータの大きさを指定します。

これで、新しいサブキーを作って値を保存できるようになりました。 使い終わったらキーをクローズします。

LONG RegCloseKey( HKEY hKey // handle of key to close );

これでキーをクローズします。成功したらERROR_SUCCESSが 返されます。失敗したときはエラーコードが返されます。

LONG RegQueryValueEx( HKEY hKey, // キーのハンドル LPTSTR lpValueName, // 呼び出す値の名前 LPDWORD lpReserved, // 予約済み(NULLを指定) LPDWORD lpType, // 値のタイプへのポインタ LPBYTE lpData, // データバッファへのポインタ LPDWORD lpcbData // データバッファの大きさへのポインタ );

これで値を読み出します。

hKeyはオープンしたキーのハンドルを指定します。

lpValueNameには、呼び出す値の名前を指定します。

lpReservedは常にNULLです。

lpTypeとか、lpcbDataはわかりにくいのですが

DWORD dwType = REG_SZ; DWORD dwByte = 32;

というようにしておいて、これらのポインタを指定します。

lpDataには値を受け取るバッファへのポインタを指定します。 データ型に注意してください。

では、プログラムを見てみましょう。

// saver03.h #define IDC_STATIC -1 #define IDC_EDIT1 2001 #define IDC_EDIT2 2003 #define ID_TIMER 32767

自前のヘッダーファイルです。

// saver03.rc #include <windows.h> #include <scrnsave.h> #include "saver03.h" ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP1 BITMAP DISCARDABLE "cat1.bmp" MYBMP2 BITMAP DISCARDABLE "cat2.bmp" ///////////////////////////////////////////////////////////////////////////// // // Dialog // DLG_SCRNSAVECONFIGURE DIALOG DISCARDABLE 0, 0, 187, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "設定" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,130,7,50,14 PUSHBUTTON "キャンセル",IDCANCEL,130,24,50,14 EDITTEXT IDC_EDIT1,5,19,82,18,ES_AUTOHSCROLL LTEXT "表示する文字列",IDC_STATIC,6,7,48,8 LTEXT "スピード(1-5000)",IDC_STATIC,7,47,50,8 EDITTEXT IDC_EDIT2,5,59,82,18,ES_AUTOHSCROLL END

今回はストリングテープルを使いません。 後は、前回にほぼ同じです。

// saver03.cpp #define hParentKey HKEY_CURRENT_USER //親キー #define lpszSubKey "Software\\Kumei\\saver03" //今回作るサブキー #include <windows.h> #include <scrnsave.h> #include "saver03.h" char szText[40]; //スクリーンセーバーに表示する文字列 int nSpeed; //SetTimerにセットする値 //レジストリに読み書きする自作関数 void GetIniDataString(char *, char *); void GetIniDataDWORD(char *, DWORD *); void SetIniDataString(char *, char *); void SetIniDataDWORD(char *, DWORD);

プログラムを簡略化するため、HKEY_CURRENT_USERやサブキーの位置を defineしておきました。また、レジストリに値を書いたり読み出したりする 関数も作ることにしました。自作関数のSetIniDataDWORDの第2引数は ポインタではなくDWORD型であることに注意してください。 (あとで関数の中身を見ればわかります)

LRESULT WINAPI ScreenSaverProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { RECT rc; HDC hdc, hdc_mem; static int wx, wy; int x1, x2, y1, y2, r, g, b, bmp_w, bmp_h; HBRUSH hBrush, hOldBrush; HBITMAP hBitmap; BITMAP bmp_info; switch(msg) { case WM_CREATE: GetIniDataString("TEXT", szText); GetIniDataDWORD("SPEED", (DWORD *)&nSpeed); GetClientRect(hWnd, &rc); wx = rc.right - rc.left; wy = rc.bottom - rc.top; SetTimer(hWnd, ID_TIMER, nSpeed, NULL); break; case WM_TIMER: r = rand() % 256; g = rand() % 256; b = rand() % 256; hdc = GetDC(hMainWindow); x1 = rand() % wx; y1 = rand() % wy; TextOut(hdc, x1, y1, szText, strlen(szText)); hBrush = CreateSolidBrush(RGB(r, g, b)); hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); x1 = rand() % wx; y1 = rand() % wy; x2 = rand() % wx; y2 = rand() % wy; Rectangle(hdc, x1, y1, x2, y2); if (r % 2 == 0) hBitmap = LoadBitmap(hMainInstance, "MYBMP1"); else hBitmap = LoadBitmap(hMainInstance, "MYBMP2"); GetObject(hBitmap, sizeof(BITMAP), &bmp_info); bmp_w = bmp_info.bmWidth; bmp_h = bmp_info.bmHeight; hdc_mem = CreateCompatibleDC(hdc); SelectObject(hdc_mem, hBitmap); x1 = rand() % wx; y1 = rand() % wy; x2 = rand() % wx; y2 = rand() % wy; BitBlt(hdc, x1, y1, bmp_w, bmp_h, hdc_mem, 0, 0, SRCCOPY); DeleteDC(hdc_mem); DeleteObject(hBitmap); SelectObject(hdc, hOldBrush); DeleteObject(hBrush); ReleaseDC(hMainWindow, hdc); break; case WM_DESTROY: KillTimer(hWnd, ID_TIMER); PostQuitMessage(0); return 0; default: break; } return DefScreenSaverProc(hWnd, msg, wParam, lParam); }

WM_CREATEのところで早速自作関数のGetIniDataStringやGetIniDataDWORD を呼び出して「表示する文字列」とか「スピード」のデータを 読み出しています。他は、前回と同じです。

BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { char szTemp[32]; switch (msg) { case WM_INITDIALOG: GetIniDataString("TEXT", szText); if(strcmp(szText, "") == 0) strcpy(szText, "粂井康孝 制作"); SetDlgItemText(hDlg, IDC_EDIT1, szText); GetIniDataDWORD("SPEED", (LPDWORD)&nSpeed); if (nSpeed == 0) nSpeed = 300; wsprintf(szTemp, "%d", nSpeed); SetDlgItemText(hDlg, IDC_EDIT2, szTemp); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: GetDlgItemText(hDlg, IDC_EDIT1, szText, sizeof(szText)); SetIniDataString("TEXT", szText); nSpeed = GetDlgItemInt(hDlg, IDC_EDIT2, NULL, FALSE); SetIniDataDWORD("SPEED", nSpeed); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }

設定ダイアログボックスのプロシージャです。 WM_INITDIALOGのところでレジストリから文字列やらスピードの値を 読み出しています。 GetPrivateProfileStringやGetPrivateProfileInt関数の 場合はデフォルトの値を指定できますが、今回の自作関数では デフォルトの値まで面倒をみていないので自分で設定します。 つまり、レジストリにまだ値が設定されていない場合は nSpeedやszTextに0, ""が設定されてしまいます。 そのような場合は300, "粂井康孝 制作"を設定するようにプログラムして おきました。

OKボタンが押されたらエジットボックスから新しい値を読み出して レジストリに書き込みます。

BOOL WINAPI RegisterDialogClasses(HANDLE hInst) { return TRUE; }

前回同様何もせずにTRUEを返します。

void SetIniDataString(char *szName, char *szData) { HKEY hKey; DWORD dwPosition; LONG lResult; RegCreateKeyEx(hParentKey, lpszSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwPosition); lResult = RegSetValueEx(hKey, szName, 0, REG_SZ, (CONST BYTE *)szData, lstrlen(szData)); RegCloseKey(hKey); return; }

自作関数の中身です。保存する値の名前と、値(文字列)を指定すると lpszKeyの位置にその値の名前と値が保存されます。

RegCreateKeyEx関数の第1引数(hParentKey)は最初にdefineした HKEY_CURRENT_USERです。同じくlpszSubKeyは"Software\\Kumei\\saver03" です。hKeyにキーハンドルが保存されます。dwPositionを調べるとキーの 新規作成か既存キーのオープンかがわかります。(ここでは調べていません)

RegSetValueExでは先ほど取得したhKeyを使ってデータを保存します。 char *型でもらってきたデータをCONST BYTE *型にキャストして 保存している点に注意してください。

void SetIniDataDWORD(char *szName, DWORD dwData) { HKEY hKey; DWORD dwPosition; RegCreateKeyEx(hParentKey, lpszSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwPosition); RegSetValueEx(hKey, szName, 0, REG_DWORD, (CONST BYTE *)&dwData, sizeof(DWORD)); RegCloseKey(hKey); return; } void GetIniDataString(char *szName, char *szValue) { HKEY hKey; DWORD dwPosition; DWORD dwType = REG_SZ; DWORD dwByte = 32; RegCreateKeyEx(hParentKey, lpszSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwPosition); RegQueryValueEx(hKey, szName, NULL, &dwType, (BYTE *)szValue, &dwByte); RegCloseKey(hKey); return; } void GetIniDataDWORD(char *szName, DWORD *dwValue) { HKEY hKey; DWORD dwPosition; DWORD dwType = REG_DWORD; DWORD dwByte = 32; RegCreateKeyEx(hParentKey, lpszSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwPosition); RegQueryValueEx(hKey, szName, NULL, &dwType, (BYTE *)dwValue, &dwByte); RegCloseKey(hKey); return; }

残り3つの自作関数を一度に表示しました。1つずつ見ていけば 別に難しくありません。 セットしたり読み出したりするデータが数値か文字列によって 関数を別々に作りましたが、1つにまとめてみてください。 その方がすっきりします。
[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 12/Jun/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。