スクリーンセーバー作成
はじめに
Windows上で動作するスクリーンセーバーを開発することを考えてみる。
なお、今となっては随分年代物となってしまったが、俺が使っている環境はWindows 2000とVisual C++ 6.0である。しかし、バージョンが上がったとしても、基本的な部分は変わらないはずである。
ヘッダファイル・ライブラリ
まず、スクリーンセーバー開発固有のヘッダファイルとして「scrnsave.h」というヘッダファイルが必要になる。
同様に、「SCRNSAVE.LIB」もしくは「SCRNSAVW.LIB」をリンクする必要がある。「SCRNSAVW.LIB」はワイドバイト文字列を取り扱うバージョンである。文字コードとしてUnicodeを使うようにするのなら(すなわち、コンパイル時の引数に_UNICODEを指定するのなら)こちらを使用する必要がある。そうでなければ「SCRNSAVE.LIB」を使う。
なお、これらのファイルはVisual Sudioについてくるから、HDD内を探せばきっとどこかに入っているはずである。
プログラムの構造
スクリーンセーバーとして動作するプログラムは、下記三つの関数を実装する必要がある。
ScreenSaverConfigureDialog
BOOL WINAPI ScreenSaverConfigureDialog(
HWND hDlg,
UINT message,
DWORD wParam,
LONG lParam
);
スクリーンセーバーの「設定ダイアログ」が表示されるときに呼ばれる。ここでダイアログを表示し、スクリーンセーバーの動きを変更できるようにする。
RegisterDialogClasses
BOOL WINAPI RegisterDialogClasses(
HANDLE hInst
);
スクリーンセーバーで使用する拡張のウインドウクラスを登録する。不要なら何もする必要はない。
ScreenSaverProc
LONG WINAPI ScreenSaverProc(
HWND hwnd,
UINT message,
DWORD wParam,
LONG lParam
);
実際のスクリーンセーバーが描画されているときの手続きを記述する。すなわち、引数hwndに渡されたウインドウハンドルに描画する処理を記述する。
以下に、これら関数について解説する。
ScreenSaverProcですること
この関数ではスクリーンセーバーの本来の処理を行います。つまり、画面上にユーザの興味をそそる魅力的なコンテンツを描き出さなければならない。
この関数には下記四つの引数が渡される。
HWND hWnd
描画対象となるウインドウのウインドウハンドルが指定される。
UINT message
ウインドウメッセージが指定される。本関数ではmessageに指定されたウインドウメッセージに応じた処理を行わなければならない。
DWORD wParam
LONG lParam
messageに指定されたウインドウメッセージを処理する為に必要となる情報が渡される。wParamとlParamの値については下記を参照のこと。
下記に、messageに指定されるウインドウメッセージと、wParam・lParamの値を示す。
| messageの値 |
意味 |
wParamの値 |
lParamの値 |
戻り値 |
| WM_CREATE |
ウインドウが構築された |
使われない |
CREATESTRUCT構造体へのポインタ |
正常に処理したら0を返す。失敗した場合は-1を返す。 |
| WM_ERASEBKGND |
背景の消去 |
デバイスコンテキストのハンドル |
使われない |
アプリケーションで背景を消去した場合は0を返す。 |
| WM_TIMER |
タイマイベント |
タイマ識別子 |
タイマコールバック関数の関数ポインタ |
正常に処理したら0を返す。 |
| WM_SIZE |
ウインドウのサイズが変更された |
サイズ変更フラグ |
ウインドウのサイズ |
正常に処理したら0を返す。 |
| WM_DESTROY |
ウインドウが破棄された |
使われない |
使われない |
正常に処理したら0を返す。 |
なお、送出されるウインドウメッセージは上記以外にも存在する。詳細はMSDNに記述されている。
上記のメッセージを処理するScreenSaverProc関数は下記のような構造となる。
LRESULT WINAPI ScreenSaverProc (
HWND hWnd,
UINT message,
DWORD wParam,
LONG lParam)
{
switch( message ){
case WM_CREATE: // 構築する
構築時に処理すべき処理を記述
return 0;
case WM_DESTROY: // 破棄する
破棄時に処理すべき処理を記述
break;
case WM_ERASEBKGND:
真を返すとWindwosによる背景消去が行われなくなる。
スクリーンセーバーであれば真を返すべきである。
return TRUE;
case WM_TIMER: // タイマ通知
一般的にはここで描画処理を行う
return 0;
case WM_SIZE: // サイズ変更通知
サイズが変わったときにしなくてはいけない処理を行う。
return 0;
}
return DefScreenSaverProc(hWnd, message, wParam, lParam);
}
|
上記のプログラム中の「DefScreenSaverProc」は、アプリケーションでは明示的に処理を行わないメッセージを処理するために呼び出す。自分で処理を実装した場合は呼ぶ必要はない。
「WM_ERASEBKGND」は、自前で背景消去処理を記述した場合には真を返す。そうすることで、Windowsが勝手に背景を白で消去処理を行わなくなる。これにより、画面が著しくチラチラする現象を防止することが可能となる。
「WM_TIMER」はタイマのイベントである。しかし、当然だがタイマーは勝手には設定されず、無論、タイマーイベントのメッセージが自動的に送出されてくることはない。すなわち、自分でタイマーの設定を行う必要がある。
タイマーの設定には下記のAPI関数を用いる。
UINT_PTR SetTimer(
HWND hWnd, // ウインドウハンドル
UINT_PTR nIDEvent, // タイマ識別子
UINT uElapse, // タイマアウト値(ミリ秒単位)
TIMERPROC lpTimerFunc // タイマプロシージャの関数ポインタ
);
hWndはウインドウメッセージを受け取るウインドウのウインドウハンドルを指定する。すなわち、ScreenSaverProc関数のhWndを指定しておけばよい。
タイマ識別子は1以上の好きな値を指定しておけばいい。タイマーを複数個設定して、複雑なイベント処理を実装しようというのであれば、タイマー識別子の値も慎重に設計する必要がある。しかし、タイマーを1つしか設定しないのであれば、全く気にする必要はない。
タイマーイベントはuElapseに指定した間隔に発生する。1秒間に24コマ描画しようと言うのであれば42程度の値を指定しておけばいい。なお、タイマーイベントの時間解像度はそれ程高くないため、余り細かいことを気にしても意味はない。
lpTimerFuncには、タイマーイベントを処理する関数の関数ポインタを指定する。なお、この引数に関数ポインタを指定した場合は、WM_TIMERのタイマーイベントは送出されなくなる。関数ポインタを指定して関数を呼び出させるか、WM_TIMERのウインドウメッセージを受け取るかの選択は、アプリケーションを作る人の個人的趣味に依存する問題である。
不要になったタイマーを破棄するには下記のAPI関数を用いる。
BOOL KillTimer(
HWND hWnd, // ウインドウハンドル
UINT_PTR uIDEvent // タイマ識別子
);
描画処理の大半はタイマーイベントの処理中で行う。すなわち、タイマーイベントが一回発生したら、1コマだけ描画を行うようにする。
まかり間違っても、無限ループを作ってその中で描画しようなどと考えてはいけない。
なお、画像は、渡されたウインドウハンドルからデバイスコンテキストを取得し、幾つかのWindowsAPIをもってして描画を行う。あるいは、OpenGLやDirectXを使用する。
ScreenSaverConfigureDialogですること
ここではスクリーンセーバーの動きをカスタマイズできるように、「設定」ダイアログを表示する手続きを記述する。
ScreenSaverProc関数と同様、ウインドウメッセージが送出されてくるので、それらを処理するような形でダイアログボックスを表示する処理を実装する必要がある。
なお、本関数からはDefScreenSaverProc関数を呼び出してはいけない。
その他すべきこと
ほかにもするべきことがいくつか存在する。
DLG_SCRNSAVECONFIGURE識別子でダイアログを定義する
スクリーンセーバーの「設定」ダイアログの識別子はDLG_SCRNSAVECONFIGUREという名前を使用することが規定されている。また、この識別子として定義される値は2003であることも規定されている。
その為、VisualC++のリソースエディタでダイアログを作る場合は、以下の手順を踏むこととなる。
- 通常通りダイアログを作成する

- 「ダイアログプロパティ」のIDを「DLG_SCRNSAVECONFIGURE」に変更する

- 「resource.h」ヘッダファイルで定義されているDLG_SCRNSAVECONFIGUREの値を2003に変更する


スクリーンセーバーの名前を定義する
「画面のプロパティ」の「スクリーンセーバ」タブを選択すると、コンボボックス中にスクリーンセーバーの日本語名称が表示される。

この名前は、Windwos98ではファイル名が使用されるようだが、Windwos2000以降ではプログラム中にリソースとして定義した文字列が使用される。
このとき使用される文字列は1番のものと決められている。よって、スクリーンセーバーの名称は下記の手順により設定を行うこととなる。
-
リソースエディタのストリングテーブルに任意のIDで名前を定義する

- 「resource.h」を開き、上記で設定したIDに対応する値を「1」にする

コンパイルする
「SCRNSAVE.LIB」をリンクすること以外は、特に注意するべき事項はない。
なお、スクリーンセーバーの拡張子は「.scr」と決められている。そのため、生成されたプログラムの拡張子を「.exe」から「.scr」に変更する必要がある。
インストール
インストール方法は特に規定されていない。
基本的にはシステムフォルダに格納されている「*.scr」ファイルをスクリーンセーバーとして認識する。よって、生成した「.scr」ファイルをシステムフォルダに格納すればいい。
また、「.scr」ファイルを右クリックして「インストール」を選択しても問題はない。

|