- 2009-07-26 (日) 9:18
- 技術
●クラスCCatWndの骨組み
まずは,「ウィンドウを出して,イベントを処理して,ウィンドウを閉じられる」という最低限の枠組みをコーディングします.
- ウィンドウプロシージャを用意する
- ウィンドウクラスの登録(RegisterClassEx)
- ウィンドウの生成と表示(CreateWindow,ShowWindow,UpdateWindow)
- ウィンドウの破棄(DestroyWindow)
- 登録したウィンドウクラスを捨てる処理が必要かも(UnregisterClass)
…と,順番通りにAPIが呼ばれるように.
各所でAPIにインスタンスハンドルを渡す必要がありますが,::GetModuleHandle( NULL );で取れるっぽいので,これを渡しています.
●ウィンドウクラスの登録
これをいつやるか? ということになりますが,とりあえず「最初にウィンドウを作ろうとしたとき」に行うことにしました.
(「最初に…」を判断するためにstaticメンバでフラグをひとつ用意して対処)
ウィンドウクラスの登録にはウィンドウプロシージャを指定する必要があるので,staticメンバ関数でウィンドウプロシージャを用意して,これを指定しておきます.
RegisterClassEx()が成功したら,上記5.のUnregisterClassをするための処理関数をatexit()で指定しておきます.
//CCatWndの宣言部抜粋 class CCatWnd { public: bool CreateWnd( ... );//ウィンドウ生成 bool DestroyWnd();//ウィンドウ破棄 private: HWND m_hWnd; //ウィンドウハンドルを覚えておく用 private: static bool ms_bRegisterClassFlag; //RegisterClassしたことを覚えておくフラグ //ウィンドウクラスの登録と削除,staticなウィンドウプロシージャ static bool RegisterClass(); static void UnRegisterClass(); static LRESULT CALLBACK WndProc__( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); }
- CreateWnd()は最初にフラグms_bRegisterClassFlagを見て,必要ならRegisterClass()をコール,その後,::CreateWindow(…WS_POPUP…)でウィンドウの生成を行います.
- RegisterClass()はWndProc__をプロシージャとしたウィンドウクラスを::RegisterClass()APIで登録し,ms_bRegisterClassFlagを書き換え,最後にatexit( CCatWnd::UnRegisterClass )しておきます.
- UnRegisterClass()はms_bRegisterClassFlagを見て,必要なら単に::UnregisterClass()APIでウィンドウクラスの登録を解除するだけです.
●ウィンドウプロシージャでメンバ変数とか触りたい
ウィンドウプロシージャWndProc__()はstaticメンバ関数なので,このままではCCatWndインスタンスに手が届かない存在(?)です.
WndProc__()に渡されてくるウィンドウハンドルを頼りに,メッセージ処理を行うべきCCatWndインスタンスを判断する機構が必要です.
要するに,CCatWndに,非staticな(普通の)メンバ関数
//オブジェクト毎のプロシージャ
LRESULT WndProc( UINT message, WPARAM wParam, LPARAM lParam );
があったら,WndProc__()が適切なオブジェクトのWndProc()をコールするようにできればOKです.
例えば,
static std::map< HWND, CCatWnd * > ms_HWND2ObjMap;
なんてものを作って管理してもよさそうですが,どうやらSetProp(),GetProp()というAPIを使えばその手間が省けるようなので,こちらを使うことにしました.
- ウィンドウが生成できたら,SetProp()でウィンドウハンドルにオブジェクトへのポインタを登録しておく
- WndProc__()では,GetProp()でオブジェクトへのポインタを取得し,そこからWndProc()を呼ぶ.
ということをすればよさそうです.
1.をどこでやるか,ですが,::CreateWindow()内でWM_CREATEメッセージが発生してくる関係上,CreateWnd()内でウィンドウを作るあたりで
m_hWnd = ::CreateWindow( … );
::SetProp( m_hWndとthisを渡して登録 );
というような記述をしてしまうと,オブジェクト毎のウィンドウプロシージャでWM_CREATEに対する処理を行うことができなくなってしまいました.
対策として,(ちょっとかっこわるいですが)CreateWnd()内で
ms_pNowCreatingObj = this;//CreateWindow()の直前にstaticメンバに,thisを代入
CreateWindow( … );
として,これからウィンドウを作るオブジェクトを覚えておいて,WndProc__()を以下のようにしました.
//ウィンドウプロシージャ //ウィンドウハンドルに登録されているオブジェクトのプロシージャに処理を渡す LRESULT CALLBACK CCatWnd::WndProc__(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { CCatWnd* pObj = NULL; if( message == WM_CREATE ) {//WM_CREATEはCreateWindow()内で来るので,特別処理. pObj = ms_pNowCreatingObj; if( pObj ) { pObj->m_hWnd = hWnd; //※ここでウィンドウハンドルをメンバ変数に設定. ::SetProp( hWnd, "THIS", (HANDLE)( pObj ) ); //オブジェクトのポインタ登録 } } else { pObj = (CCatWnd*)( GetProp( hWnd, "THIS" ) ); } // if( pObj ) { return pObj->WndProc( message, wParam, lParam ); } else { return DefWindowProc( hWnd, message, wParam, lParam ); } }あとでms_pNowCreatingObjを排他処理で守るようにすれば安全…かな?
とにかくこれでCCatWndオブジェクトが自分で作ったウィンドウへのメッセージを処理できる枠組みができました.
あとは,コンストラクタで適切な初期化をし,WM_CREATE時あたりでレイヤードウィンドウの設定を行い,デストラクタやWM_DESTROY時の処理あたりに必要な後始末コードを追加したりしておきます.
- Newer: 猫ウィンドウクラスを作る(3)
- Older: 猫ウィンドウクラスを作る(1)