第128章 スクリーンセーバー その2


今回は、「画面のプロパティ」「スクリーンセーバー」の 設定ボタンを押したときに表示されるダイアログボックスを 作ります。このダイアログボックスでいろいろな設定を します。設定した内容はファイルに記録されて、 次回起動したときも有効です。



前回のプログラムを見て、自分でScreenSaverConfigureDialog 関数を書いてもうまく、ダイアログボックスが表示されずに 苦労した方はいませんか。実は、ちょっとしたことで なかなかわからないバグが生じます。 ダイアログボックスのデザインをリソースエジタで作って そのまま使うと失敗します。 ダイアログボックスのIDをDLG_SCRNSAVECONFIGUREとしますが、 普通にリソースエジタを使うとこのIDに勝手な整数が 割り振られてしまいます。こうなると、なかなか間違いが わかりません。DLG_SCRNSAVECONFIGUREには2003が割り振られないといけません。 これ以外の整数が割り振られると「設定ボタン」を押しても ダイアログボックスは出てきません。この値はどうしてわかるかというと scrnsave.hを見れば書いてあります。その他のシンボル値も 勝手に番号が振られては都合が悪いわけです。

1.リソースエジタでダイアログボックスのデザインをする 2.リソーススクリプトの不要部分を削除 3.リソース・スクリプトにwindows.h, scrnsave.hをインクルード 4.シンボル値などは自分でヘッダファイルを作る 5.自分で作ったヘッダファイルはソースファイル、リソース・スクリプトの   両方にインクルードする

ま、これでうまく表示されるはずです。では、今回のプログラムを 見てみましょう。

// saver02.rc #include <windows.h> #include <scrnsave.h> #include "saver02.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 ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE PRELOAD DISCARDABLE BEGIN idsIniFile "saver02.ini" idsAppName "Screen Saver.saver02" END

リソース・エジタでダイアログボックスをデザインして 不要部分を全部きれいさっぱり削除しました。

ビットマップは前回使用したものと同じものを使います。

また、このスクリーンセーバーの名前と、設定を保存するファイル名を 文字列テーブルに設定します。idsIniFile, idsAppNameは scrnsave.hで定義されています。

スクリーンセーバーの名前は16ビット時代の名残で "Screen Saver.名前"と言うよう設定します。ピリオドより右側が このセーバーの固有の名前です。Windowsのディレクトリに control.iniというファイルがありますが、これをのぞいてみると [Screen Saver.*****]というセクションがいくつかあると思います。 そこに自分のセーバーの固有の設定を保存していました。 Windows95ではレジストリに保存するよう勧められています。 しかし、ユーザーの中にはレジストリに書き込まれるのを 極端に嫌う人もいます。そこで今回は自分のiniファイル(saver02.ini)を作って これに保存することにします。不要になったらこのファイルを そのまま消してしまえばよいわけです。

さて、今回はresource.hを使いませんからscrnsave.hで定義されているもの 以外のシンボル値は自分で設定します。

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

さて、これだけではイメージがわかないので実際のダイアログボックスを 示します。

「表示する文字列」に適当な文字列を入力します。 ビットマップの他にこの文字列もランダムな位置に表示されます。

「スピード」にはWM_TIMERメッセージを発生させる時間間隔(ミリ秒) を入力します。間違って2バイト文字を入力するとまずいのですが プログラムを簡単にするために特に、入力文字の制限などは付けていません。 エジットボックスではなくてスライダーなどを付けるのが望ましいでしょう。

では、このスクリーンセーバーが実際に動いているときの様子を示します。

大してきれいなものではありません。いろいろ工夫してみてください。



では、ソースファイルを見てみます。

// saver02.cpp #include <windows.h> #include <scrnsave.h> #include "saver02.h" char szText[40]; //スクリーンセーバーに表示する文字列 int nSpeed; //SetTimerにセットする値 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: LoadString(hMainInstance, idsAppName, szAppName, APPNAMEBUFFERLEN); LoadString(hMainInstance, idsIniFile, szIniFile, MAXFILELEN); GetPrivateProfileString(szAppName, "TEXT", "粂井康孝 制作", szText, sizeof(szText), szIniFile); nSpeed = GetPrivateProfileInt(szAppName, "SPEED", 500, szIniFile); 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メッセージが来たら文字列テーブルからアプリケーション名、 INIファイル名を呼び出しています。szAppName, APPNAMEBUFFERLEN, szIniFile, MAXFILELENはscrnsave.hで定義されています。 LoadString関数は第64章を参照してください。 これで、アプリケーション名とINIファイル名がszAppName, szIniFileに 格納されました。次にこれらの名前を使ってINIファイルから前回の 設定を呼び出します。

DWORD GetPrivateProfileString( LPCTSTR lpAppName, // セクション名 LPCTSTR lpKeyName, // キー名 LPCTSTR lpDefault, // デフォルト文字列 LPTSTR lpReturnedString, // 文字列を受け取るバッファ DWORD nSize, // バッファサイズ LPCTSTR lpFileName // ファイル名 );

これは、長い名前の関数ですが説明はあんまり必要ないですね。 セクション名は[***]というようにかっこで囲まれています。

キー名とはセクション名の下にある設定項目です。

デフォルト文字列は、指定したファイルがないときなど 失敗したときにバッファに入れる文字列を指定します。

この関数が成功したときはコピーした文字数(最後のヌル文字は 含みません)

INIファイルは

[section] key1=文字列1 key2=文字列2 ...

というような感じになっています。

UINT GetPrivateProfileInt( LPCTSTR lpAppName, // セクション名 LPCTSTR lpKeyName, // キー名 INT nDefault, // キーが見つからないときのデフォルト LPCTSTR lpFileName // ファイル名

ま、これもあんまり説明がいりませんが、得られた整数値が 戻り値となります。戻り値はUINT型であることに注意してください。 さて、これでINIファイルから表示する文字列とスピードが 読み込まれました。

WM_TIMERメッセージが来たときの処理は前回とほぼ同じです。 テキストを表示する部分が増えただけです。 WM_DESTROYメッセージのところでタイマーを殺すのを忘れないで ください。

BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { char szTemp[32]; switch (msg) { case WM_INITDIALOG: LoadString(hMainInstance, idsAppName, szAppName, APPNAMEBUFFERLEN); LoadString(hMainInstance, idsIniFile, szIniFile, MAXFILELEN); GetPrivateProfileString(szAppName, "TEXT", "粂井康孝 制作", szText, sizeof(szText), szIniFile); SetDlgItemText(hDlg, IDC_EDIT1, szText); nSpeed = GetPrivateProfileInt(szAppName, "SPEED", 500, szIniFile); 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)); WritePrivateProfileString(szAppName, "TEXT", szText, szIniFile); nSpeed = GetDlgItemInt(hDlg, IDC_EDIT2, NULL, FALSE); wsprintf(szTemp, "%d", nSpeed); WritePrivateProfileString(szAppName, "SPEED", szTemp, szIniFile); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; } BOOL WINAPI RegisterDialogClasses(HANDLE hInst) { return TRUE; }

ScreenSaverConfigureDialogプロシージャは普通のダイアログボックスの プロシージャとほぼ同じです。

しかし、注意する点があります。通常のプログラムであれば 親ウィンドウのWM_CREATEメッセージでグローバル変数に値を 設定したならダイアログボックスのプロシージャからもこの 値を読めるはずです。しかし、スクリーンセーバーではこのプロシージャで もう一度ファイルから値を読み出しておく 必要があります。

WM_INITDIALOGメッセージのところで改めてINIファイルから 前回の設定を読み出しています。そして、エジットボックスに 表示しています。スピードも文字列としてエジットボックスに 表示している点に注意してください。

OKボタンが押されたときは、ダイアログボックスの 各入力を読み出して、INIファイルに書き込んでいます。

BOOL WritePrivateProfileString( LPCTSTR lpAppName, // セクション名 LPCTSTR lpKeyName, // キー名 LPCTSTR lpString, // 書き込む文字列 LPCTSTR lpFileName // ファイル名 );

これも、あんまり説明は必要ないですね。 成功したらTRUEを失敗したらFALSEを返します。

ところで、WritePrivateProfileIntという関数は ありません。数値も文字列として書き込みます。

キャンセルボタンが押されたら何もせずにダイアログ ボックスを閉じます。

RegisterDialogClasses関数はダイアログボックスに 特別な設定をしない限り単にTRUEを返すだけです。

さて、今回でやっとスクリーンセーバーらしくなってきました。 ランダムに表示されるだけでなくアニメーションの ようにビットマップが画面を動き回るものに作り替えてみて ください。


[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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