ウィンドウプロシージャの戻り値
ウィンドウプロシージャの戻り値はLRESULT(LONG_PTR)であり普通は0を返す。この0を返すというのはどういう意味があるのかつい最近までわかっていなかった。またメッセージにかかわらず0を返せば良いというものではないということもつい昨日知ったのである。
例えばWM_NCCREATEメッセージをアプリ側でハンドルした場合の戻り値は0ではなくTRUE(1)を返す。ここでFALSE(0)を返すとWindow生成処理が破棄され、ウィンドウハンドルがNULLとなる。それに対してWM_CREATEメッセージはアプリ側でハンドルした場合の戻り値は0である。-1を返すとWindow生成処理が破棄され、ウィンドウハンドルがNULLとなる。しかし何故同じ挙動なのにWM_NCCREATEとWM_CREATEでは値が違うのかは不明である。
他のメッセージ、例えばWM_SIZEを見てみるとMSDNではこう書いてあった。
If an application processes this message, it should return zero.
「処理した場合は0をセットすべき」と書いてある。推測するに処理しなかった場合は0以外をセットするのだろうけれど、その場合OSはどのような挙動をするのだろうか。少しググッてみたけどよくわからなかった。
WM_NCCREATEメッセージ以外は概ねアプリ側でハンドルした場合は0を返すで良いようである。
ダイアログプロシージャの戻り値
ダイアログプロシージャはウィンドウプロシージャとは似て非なるものである。何が違うかというと戻り値である。ダイアログプロシージャの戻り値はINT_PTR型であり、処理した場合はTRUE(1)、処理しなかった場合はFALSE(0)を返す。また未処理のメッセージをDefWindowProcしてはいけない。ただFALSEを返せばOSがそれなりに処理してくれる。
ウィンドウラッパクラスのウィンドウとダイアログでの共用
私は自前のウィンドウラッパクラスを作っている。ウィンドウベースとダイアログベースで共用できるようにしたいと考えている。どうするかというとメッセージ処理関数の型をテンプレート引数にしている。この引数の型によりウィンドウ・ダイアログ固有の処理を書き分けるのである。
/** window ベースクラス */
template <typename ProcType = WNDPROC>
struct base_win32_window : public base_window
{
typedef ProcType proc_t;
例えば未処理のプロシージャの戻り値はウィンドウプロシージャ(WNDPROC)であれば::DefWindowProc()の戻り値、ダイアログプロシージャ(DLGPROC)であればFALSEを返すようなコードは下記となる。
// 普通はDefWindowProcW
template <typename ProcType>
inline LRESULT def_wnd_proc(HWND hwnd,uint32_t message, WPARAM wParam, LPARAM lParam)
{
return ::DefWindowProcW(hwnd,message,wParam,lParam);
};
// DLFPROCはTRUEを返すように特殊化。メタプロでいうところのif(ProcType == DLGPROC)という条件処理に相当。
template <>
inline LRESULT def_wnd_proc<DLGPROC>(HWND hwnd,uint32_t message, WPARAM wParam, LPARAM lParam)
{
return FALSE;
};
/** window ベースクラス */
template <typename ProcType = WNDPROC>
struct base_win32_window : public base_window
{
.
.
.
virtual LRESULT other_window_proc(HWND hwnd,uint32_t message, WPARAM wParam, LPARAM lParam)
{
return def_wnd_proc<ProcType>(hwnd,message,wParam,lParam);// テンプレートパラメータで処理わけ
};
.
.
.
本当はクラステンプレートで特定のメンバ関数を特殊化できればいいのだけれど前試した時出来なかったのでテンプレート関数にしている。
ここで気になるのはDLGPROCでもINT_PTRでなくLRESULTで値を返しているところであるが、64bit環境だとWNDPROC型とDLGPROC型は引数も戻り値も同一になることを利用(悪用?)して同じ関数で処理している。ただWNDPROCもDLGPROCも戻り値は違う名前でtypedefされているので将来的に同じであるという保証はない。
今のところ動いているけど、Windowsのお作法にきちんと従っているか全く自信がない。これから作っていくうちにいろいろお作法を知っていくだろうから、おかしければ都度修正を加えていくことにしている。