ウインドウプロシージャをクラスの中に取り込むべきか否かという問題について、相当悩んだのである。処理やユーザの扱い上、全然問題が無いのであれば、迷う事は全く無かったのである。クラス内にウインドウプロシージャを取り込む方法については、既に試作実験も終わっているし、技術的には可能である事が分かっている。
- 速度的な問題
悩んだ原因の一つは、速度的な問題である。ウインドウプロシージャの様な、コールバック関数を、クラス内に取り込む為には、すんなりアドレスを渡してくれないので、幾つかの手続きを踏んだ上で、そのクラスから生成された、全てのウインドウが、唯一の存在であるウインドウプロシージャを共有し、そのプロシージャの中で、各クラス内のプロシージャに分岐する方法を採るのだ。さて、今,自分のウインドウ宛に、メッセージイベントが生成されたとしよう。普通ならば、そのウインドウのウインドウプロシージャがダイレクトに呼ばれて処理を行う事になるが、クラスの中に、ウインドウプロシージャが有る場合には、一旦共通のウインドウプロシージャが呼ばれ、登録されたウインドウパラメータから、クラスインスタンスの実体(アドレス)を取得し、そのクラスの中にあるウインドウプロシージャを呼ぶ(…長いな)という手順を採るのである。また、最初に呼ばれた時(WM_CREATE)には、それに加えて、最初に到着するlParamからインスタンス化されたクラスの実体を取り出して、ウインドウパラメータにセットするという処理も加わるのである。即ち、ただでさえ数段階,初回は更に数段階の処理が、本来のウィンドウプロシージャに到達するまでに掛かるわけだ。メッセージが殆ど上がらない簡単なアプリならば問題無いが、もし、超多忙なウインドウアプリを造るとしたら、ただでさえ遅くなるクラス化では、速度的には問題が出てしまうのである。
- 速度的な問題の解決方法
一番簡単な解決策は、ユーザが選択できる方法である。例えば、CoolFrameWnd()のコンストラクタや初期化関数の呼ばれ方で切り替える方法、若しくは、もう一つ、CoolFrameWnd同様のクラスを宣言し、こちらを使えば、クラス内のプロシージャが使えるという方法で、どちらを使うかは、ユーザに決めてもらうという事である。ここでは、下記事情も有って、後者の方法を採用した。クラス名は、拡張版という意味も含めて、CoolFrameWndExである。
- 継承させなければ使えない
クラスライブラリに用意出来るウインドウプロシージャは、殆ど何も出来ない役立たずって事になる。つまりは、継承してウインドウプロシージャをオーバライドするとか、専用に用意した関数をオーバライドするとかしなければならないのである。従って、このクラスは、完全に継承を意識したクラスとして設計しなければならないのである。元々CoolFrameWndクラスは、継承を意識したクラスなのだが、この様な継承(継承前のクラスをウインドウプロシージャに認識させるので、宣言は、基本クラス,インスタンス化は、派生クラスという方法にならなければならない)の場合、ちょっと趣向を変えないと、いけないのである。
- ユーザが使うメッセージについて、
継承して、ウインドウプロシージャ自体を全部書き直すなら、別個にプロシージャを用意するのとあまり変わらない。ちょっと良いところが有るとすれば、デフォルトの部分が有るから、WM_CREATEと、WM_DESTROY辺りは、基底クラスを呼んでしまえば良いって位だろうか?これではクラス化した意味が殆ど無いので、ユーザが良く使うメッセージについては、基底クラスに於いて、別個に仮想関数にてハンドラを用意しておいて、それを呼ぶ様にしておこうと思う。勿論,ウインドウプロシージャも仮想関数だから、それをオーバライドしてしまえば、全てを設計出来る訳だし、もし、そこまで必要無ければ、各ハンドラをオーバライドすれば良いのである。
- WM_CREATE
virtual void OnCreate(HWND hwnd,WPARAM wParam,LPARAM lParam) ;
基底クラスでは何もしない。但し、このメッセージは、親WindowのCreateWindowExが実行された場合に発生するので、クラスの構造上、この時点で、WindowNumberは登録されていない。ここで別クラスを利用して、部品を配置しようとすると、WindowNumber=0にてアプリのインスタンスを聞き出して、hwndを使って初期化等と、面倒になるので、実質上、利用範囲が狭くなる。従って、
virtual void OnCreated(HWND hwnd,int WindowNumber) ;
を定義する。このメンバ関数は、CoolFrameWndExクラスが、WindowNumberを取得した直後に呼ばれるものとし、基底クラスでは何もしないものとする。ここでは、WindowNumberがそのまま使用出来るので、起動時に配置しておきたい部品等を、そのまま定義する事が出来る。
- WM_DESTROY
virtual void OnDestroy(HWND hwnd,WPARAM wParam,LPARAM lParam) ;
基底クラスでは、PostQuitMessage()を行う。
- WM_SIZE
virtual void OnSize(HWND hwnd,WPARAM wParam,LPARAM lParam) ;
基底クラスでは、何もしない。
- WM_PAINT
virtual void OnPaint(HWND hwnd,HDC hdc) ;
基底クラスでは、何もしない。BeginPaintとEndPaintについては、
基底クラスのプロシージャが行う。
- WM_USER
virtual void OnUser(HWND hwnd,WPARAM wParam,LPARAM lParam) ;
基底クラスでは、何もしない。
- WM_COMMAND
virtual void OnCommand(HWND hwnd,WPARAM wParam,LPARAM lParam) ;
基底クラスでは、何もしない。
- サンプル
サンプル01のソースを、そのままCoolFrameWndExに置き換えてみた。メインのプログラムから、完全にウインドウプロシージャが消え、代わりに変なクラスが作成されている。このクラスが、CoolFrameWndExを継承した実体である。どうだろうか?自分としては、メッセージの流れとか、色々見えている状態で、必要な部分だけを拡張出来るという自分好みのクラスになったと思うのだが…。
サンプル02のソースコード
ついでだが、これで、HelloWorldを考えたら、とても簡単なコードに出来る筈だと思って書いてみた。ざっと40行(HTMLにする為に、若干折り曲げしているが、元は40行である)…これならプログラムする気力も出てくるだろう。
// ---------------------------------------------------
// CoolFrameWndExを使用したHelloWorld
#include <windows>
using namespace std ;
#include "coolbsc.h"
#include "coolpart.h"
class CMyMain : public CoolFrameWndEx{
public:
CMyMain(HINSTANCE,HINSTANCE,char *,int) ;
virtual ~CMyMain(){} ;
virtual void OnPaint(HWND,HDC) ;
} ;
CMyMain::CMyMain(HINSTANCE hi,HINSTANCE hpi,char *cname,int nCS)
: CoolFrameWndEx(hi,hpi,cname,nCS)
{
}
void CMyMain::OnPaint(HWND,HDC hdc)
{
TextOut(hdc,0,0,"Hello World! Part2",18) ;
}
// --- WindowMainFunction -------------------------
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR,int nCmdShow)
{
WPARAM retv ;
CoolFrameWndEx *MainWin ;
MainWin=new CMyMain(hInstance,hPrevInstance,
"HelloWnd",nCmdShow) ;
MainWin->Regist() ;
MainWin->Create("TestProc for CoolLib",400,200,TRUE) ;
MainWin->Show() ;
retv=MainWin->MessageLoop() ;
delete MainWin ;
return retv ;
}
|
|