最近の記事
- 4/22 - 思い出の紙時計
- 3/29 - さくらインターネットの VPS リニューアル
- 3/19 - SSHD への攻撃を分析してみた
- 3/9 - キーボードの過酷さ
- 3/8 - .NET のパフォーマンスについて
- 3/7 - phpMyAdmin への攻撃
- 3/1 - ミイラ取りがミイラになりかけた
- 2/22 - URL を知らなければ安全だと?
- 2/16 - 決意
- 1/30 - ルーターの UPnP 対応状況
Entering Passive Mode
ISaver を実装しているプラグインが複数ある場合、
実行に使うプラグインを決めなければならない。
これを決めるのはもちろん利用者なので、
その設定を行うプラグイン管理画面が必要となる。
ということで、ホストのダイアログボックスを改造し、
好きなプラグインを選択できるようにしてみよう。
リソースファイルを書き換え、
スクリーンセーバーの名前と、
ダイアログボックスの定義を書き換える。
プラグインを動的に読み込む所から考えてみよう。
現在 Dispatcher.cpp では、#using ディレクティブを使い、
<HelloSaver.dll> を直接参照しているため、
以下のコード断片だけでインスタンスを作成できる。
// スクリーンセーバーを作成
HelloSaver saver;
これを動的に読み込むように切り替えるためには、
まず、HelloSaver.dll への依存を外さなければならない。
そうなると、「HelloSaver」のように、
プラグイン実装のクラス型を直接使うことはできないため、
リフレクションを使ってアクセスすることになる。
次のステップに進むか。
現状のホストには大きな制限がある。
それはリソースの問題だ。
スクリーンセーバーの名前とアイコンは、
ホストに Windows のリソースとして埋め込まれる。
これらは、プラグインで置き換えることはできない。
これにより生じる制約は以下の 2 つ。
・表示するスクリーンセーバーの名前を変えられない
・スクリーンセーバーのアイコンを変えられない。
描画コードとの連携も完成したので、
ちゃんと動作するかテストしてみる。
実行してみると、画面の中央に、
「Hello World!」と表示されたことが確認できた。
……これだけだと短すぎるので、もう少し書くか。
このスクリーンセーバーの実行は
C++ ネイティブアプリと違い、起動に多少時間がかかる。
突如現れた delete 構文。
この delete の意味するのは何なんだろうか。
まず、昨日のコードから、大事な部分を引用していこう。
Graphics ^g = Graphics::FromHdc(...);
try {
...
} finally {
delete g;
}
Create/Destroy の準備ができたので、
本題となる Dispatcher::Paint の実装を行う。
void Dispatcher::Paint(HWND hwnd) {
// 描画開始
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
try {
GCHandle::Alloc でハンドルに ID を割り当てると、
数値として取り扱えるようになるので便利だが、
不要になった時にはちゃんと解放しなければならない。
これを怠ると、ハンドルへの参照が残ってしまうので、
オブジェクトがガベッジコレクションされなくなり、
メモリリークを引き起こす要因となる。
GCHandle::Alloc で参照したハンドルを解放するためには、
どこかで GCHandle::Free を呼ぶ必要がある。
GCHandle を使って割り当てた ID はただの数値なので、
利用するときにはハンドルに戻さなければならない。
ID からハンドルに戻すのは、
ハンドルから ID に変換した時と逆の手順を踏めば良い。
// ウィンドウのユーザデータから ID を取得
LONG_PTR value = GetWindowLongPtr(hwnd, GWLP_USERDATA);
void *pointer = reinterpret_cast<void *>(value);
ハンドル型の値は演算も変換もできないため、
そのままではスコープを超えて、
オブジェクトを保持することが困難である。
グローバル領域に変数を確保し、
ハンドル型の値を保持する手はあるが、
変数の可視性を高めてしまうのは、
保守性に劣り、見通しも悪くなるのでやりたくない。
ではどうすれば良いか。
次に作るのは、描画コードの呼び出しだ。
Configure メソッドを作成した際は、
「HelloSaver saver;」という形で変数を宣言した。
この場合、saver 変数のスコープは、
Configure メソッドの内部なので、
メソッドから戻る直前に変数がスコープから外れ、
自動的にデストラクタが呼び出されて解放される。
でも、描画コードの呼び出し部分の実装は、
Configure のような単発の呼び出しではなく、
Create => Paint => Destroy の流れとなる。