|
本サイトは移転しました。旧アドレスからのリダイレクトは2025年03月31日(月)まで有効です。
|
🛈 | ✖ |
wxWidgetsライブラリがウィンドウズのメッセージハンドラをどのように実装するかをソースコードで確認する。
最新バージョンと過去バージョンは以下リンクからダウンロードできる。本記事調査はバージョン3.1.0ソースコードを用いた。
wxWidgetsを利用するソースコード例として動作確認(1)のDesktop1プロジェクトを用いる。メインウィンドウクラスDesktop1FrameはDesktop1Frame.hに定義され、継承はDesktop1Frame→wxFrame→wxFrameBase→wxTopLevelWindow→wxTopLevelWindowNative→wxTopLevelWindowBase→wxNonOwnedWindow→wxNonOwnedWindowBase→wxWindowである。wxWidgetsのウィンドウズ実装でwxTopLevelWindowNativeはwxTopLevelWindowMSW(include\wx\toplevel.h:329)に#defineされ、wxWindowはwxWindowMSW(include\wx\window.h:1932)に#defineされる。ソースコードでwxWindowMSWとされているものはクライアントコードから通常はwxWindowで参照される。
Desktop1Frameは基底クラスのwxFrameを2ステップ構築するためDesktop1Frame.cppのコンストラクタ内でwxFrame::Createをコールする。
wxWindowを継承するウィンドウ(コントロールを含む)はウィンドウズAPIが定義するウィンドウハンドルを内部データとして持ち、そのメッセージハンドラとなるウィンドウプロシージャを定義する。ほどんどのwxWindow派生クラスは引数を持つコンストラクタによる1ステップ構築と、引数を持たないデフォルトコンストラクタとCreateメンバ関数による2ステップ構築を用意する。1ステップ構築もコンストラクタ内でCreateをコールする。wxFrame(include\wx\msw\frame.h:18)の場合を示す。
wxFrame::Create(src\msw\frame.cpp:117)はwxTopLevelWindowMSW::Create(src\msw\toplevel.cpp:414)をコールする。Desktop1プロジェクトはフレームベースなのでCreateはwxTopLevelWindowMSW::CreateFrame(src\msw\toplevel.cpp:398)をコールする。CreateFrameはさらにwxWindowMSW::MSWCreateをコールする。MSWCreate(src\msw\window.cpp:3630)がウィンドウハンドルを生成しウィンドウプロシージャを定義する。
メインウィンドウがダイアログベースの場合はwxTopLevelWindowMSW::CreateDialog(src\msw\toplevel.cpp:326)をコールする。CreateDialogはMSWCreateをコールせず自らウィンドウハンドルを作成しウィンドウプロシージャを定義する。
コントロール(wxControl派生クラス)の場合はケースバイケースとなるが、普通は自らのCreate関数からウィンドウクラス名を指定してwxWindowMSW::MSWCreateControl(src\msw\control.cpp:102)をコールし、MSWCreateControlがウィンドウハンドルを作成しウィンドウプロシージャを定義する。標準コントロール(例えばwxButton)ならウィンドウクラス名に標準名(Button)を指定してウィンドウズが事前定義するコントロールのウィンドウハンドルを作成する。自らのCreate関数を持たない場合はwxWindowMSW::Create(src\msw\window.cpp:478)がwxTopLevelWindowMSW::CreateFrameと同様にMSWCreateをコールし、wxFrameと同じウィンドウクラスとなってしまう。
まとめればwxWindow派生クラスは構築時にMSWCreate、CreateDialog、MSWCreateControlのいずれかでウィンドウハンドルを作成しウィンドウプロシージャを定義する。
ウィンドウハンドルの作成についてまとめる。wxFrameのウィンドウクラス名(wxWindowあるいはwxWindowNR)はwxWidgets定義で、そのウィンドウクラスはwxWindowMSW::MSWGetRegisteredClassNameが作成する。wxDialogの奇妙なウィンドウクラス名はウィンドウズ標準ダイアログのウィンドウクラス名である。
基底クラス | クラス | wxWidgets関数 | ウィンドウズAPI | ウィンドウクラス名 |
---|---|---|---|---|
wxTopLevelWindow | wxFrame | wxWindowMSW::MSWCreate | CreateWindowEx | wxWindowNR |
wxDialog | wxTopLevelWindowMSW::CreateDialog | CreateDialogIndirect | #32770 | |
wxControl | 標準(例えばwxButton) | wxWindowMSW::MSWCreateControl | CreateWindowEx | 標準(例えばButton) |
カスタム(Create関数定義) | wxWindowMSW::MSWCreateControl | CreateWindowEx | カスタム |
wxWindowMSW::MSWGetRegisteredClassName(src\msw\window.cpp:472)は"wxWindow"を実引数に与えてwxApp::GetRegisteredClassName(src\msw\app.cpp:614)をコールする。GetRegisteredClassNameは与えられた名前(例えばwxWindow)を持つウィンドウクラスが存在しない場合、リサイズ時に再描画するウィンドウクラス(wxWindow)と再描画しないウィンドウクラス(wxWindowNR)をウィンドウズAPIのRegisterClassで作成する。関数はリサイズ再描画する方のウィンドウクラス名(wxWindow)を返す。
MSWCreate、MSWCreateControl、CreateDialogはCreateWindowExあるいはCreateDialogIndirectでウィンドウハンドルを作成した後に、共通のwxWindowMSW::SubclassWinをコールしてウィンドウプロシージャを定義する。wxWindowMSW::MSWCreateの場合を示す。
全てのwxWindow派生クラスはwxWindowMSW::SubclassWin(src\msw\window.cpp:1092)をコールして同じwxWndProcをウィンドウプロシージャに定義する。wxFrameのようにウィンドウクラスをwxApp::GetRegisteredClassNameで作成した場合はwxWndProcが既に定義されていて冗長だが、SubclassWinが正しく対応する。wxSetWindowProc(include\wx\msw\private.h:1125/1149)とwxGetWindowProc(include\wx\msw\private.h:1115/1139)は32ビット/64ビットに従いウィンドウズAPIのSetWindowLong/SetWindowLongPtrとGetWindowLong/GetWindowLongPtrで指定されたウィンドウハンドルのウィンドウプロシージャを定義あるいは取得する。
wxWndProc(src\msw\window.cpp:2632)はウィンドウハンドルからwxWindowポインタを取得し、派生クラスがオーバーライドするMSWWindowProc仮想メンバ関数をコールする。このMSWWindowProcはマニュアルに記載されていない。wxWindowポインタを取得できない(wxWidgets管理ウィンドウでない)場合、あるいはwxGUIEventLoop::AllowProcessing(include\wx\msw\evtloop.h:48)がfalse(クラッシュレポートなどのクリティカルウィンドウがメッセージ処理抑制)の場合はウィンドウズAPIのDefWindowProcデフォルトウィンドウプロシージャをコールする。
wxWindow派生クラスはwxWindowMSW::MSWWindowProc(src\msw\window.cpp:3251)のオーバーライドでウィンドウズメッセージ処理を任意に変更できる。ソースコードコメントはMSWWindowProcオーバーライドではなくコールするwxWindowMSW::MSWHandleMessage(src\msw\window.cpp:2676)オーバーライドを推奨している。MSWWindowProcはMSWHandleMessageがメッセージ処理を行わない場合はwxWindowMSW::MSWDefWindowProcをコールする。MSWHandleMessage、MSWDefWindowProc共にMSWWindowProcと同じくマニュアルに記載されていない。
wxWindowMSW::MSWDefWindowProc(src\msw\window.cpp:2205)仮想メンバ関数はSubclassWinで記憶した古いウィンドウプロシージャをコールし、そのようなウィンドウプロシージャが無い場合(例えばwxFrameの場合)はDefWindowProcをコールする。
wxFrame::MSWWindowProc(src\msw\frame.cpp:825)を示す。
ここで処理されないメッセージはwxTopLevelWindowMSW::MSWWindowProc(src\msw\toplevel.cpp:258)とwxWindowMSW::MSWWindowProcがコールするwxWindowMSW::MSWHandleMessageで処理される。以下がウィンドウズメッセージ処理のほぼ完全なリストである。processedは処理完了フラグで多くは処理結果が代入されるが、いくつかの場合でtrueまたはfalseに固定される。processedがfalseの場合は基底クラスが引き続き処理を実行する。処理関数のうちHandleXXXとDoXXXは各クラスの仮想でないメンバ関数で、MSWOnXXXはマニュアルに記載されない仮想メンバ関数である。HandleXXXの多くはwxWidgetsイベントを生成してイベントハンドラに処理を委ねる。HandleNotifyはMSWOnNotify仮想メンバ関数をコールする。通常のカスタマイズはwxWidgetsイベントハンドラ、あるいは派生クラスにおけるMSWOnXXXを含む仮想メンバ関数オーバーライドで実施する。ウィンドウズメッセージ処理を直接カスタマイズするにはMSWWindowProcかMSWHandleMessageをオーバーライドするが、ほとんどのメッセージはMSWHandleMessageでオーバーライドできる。いくつかのメッセージ(例えばWM_SIZE)はMSWWindowProcが処理するためこちらをオーバーライドする事になるが、これらはwxWidgetsイベント(wxEVT_SIZE)処理でカスタマイズできるため必要性は少ない。
関数 | ウィンドウズメッセージ | 処理 | processed | 備考 | |
---|---|---|---|---|---|
wxFrame::MSWWindowProc | WM_CLOSE | !Close(...) | - | ||
WM_SIZE | HandleSize(...) | - | |||
WM_COMMAND | HandleCommand(...) | true | - | ||
WM_QUERYDRAGICON | ドラッグアイコン取得 | - | |||
wxTopLevelWindowMSW::MSWWindowProc | WM_SYSCOMMAND | SC_MINIMIZE | DoSaveLastFocus(...) | false | - |
SC_RESTORE | DoRestoreLastFocus(...) | true | 先に基底MSWWindowProcをコール | ||
SC_SIZE未満 | m_menuSystem->MSWCommand(0,id) | true | システムメニューカスタマイズ | ||
wxWindowMSW::MSWHandleMessage | WM_CREATE | HandleCreate(...) | - | ||
WM_DESTROY | HandleDestroy(...) | false | - | ||
WM_SIZE | HandleSize(...) | - | |||
WM_MOVE | HandleMove(...) | - | |||
WM_MOVING | HandleMoving(...) | - | |||
WM_ENTERSIZEMOVE | HandleEnterSizeMove(...) | - | |||
WM_EXITSIZEMOVE | HandleExitSizeMove(...) | - | |||
WM_SIZING | HandleSizing(...) | - | |||
WM_ACTIVATEAPP | wxTheApp->SetActive(...) | false | - | ||
WM_ACTIVATE | HandleActivate(...) | - | |||
WM_SETFOCUS | HandleSetFocus(...) | - | |||
WM_KILLFOCUS | HandleKillFocus(...) | - | |||
WM_PRINTCLIENT | HandlePrintClient(...) | - | |||
WM_PAINT | HandlePaint(...) | - | |||
WM_CLOSE | - | true | rc.result=true、~wxWindowに任せる | ||
WM_SHOWWINDOW | HandleShow(...) | - | |||
WM_MOUSEMOVE | HandleMouseMove(...) | - | |||
WM_MOUSELEAVE | 過剰イベントのフィルター | false | (理解困難) | ||
WM_MOUSEWHEEL など | HandleMouseWheel(...) | - | |||
WM_LBUTTONDOWN など | HandleMouseEvent(...) | - | |||
MM_JOY1MOVE など | HandleJoystickEvent(...) | - | |||
WM_COMMAND | HandleCommand(...) | - | |||
WM_NOTIFY | HandleNotify(...) | MSWOnNotifyをコール | |||
WM_DRAWITEM | MSWOnDrawItem(...) | trueならrc.result=true | |||
WM_MEASUREITEM | MSWOnMeasureItem(...) | trueならrc.result=true | |||
WM_GETDLGCODE | キー入力設定をrc.resultに代入 | 備考 | 標準コントロール以外でwxWANTS_CHARS | ||
WM_KEYDOWN など | HandleKeyDown(...) | 特殊キーならwxEVT_CHARを明示生成 | |||
WM_KEYUP など | HandleKeyUp(...) | - | |||
WM_CHAR など | HandleChar(...) | WM_KEYDOWN処理があれば何もしない | |||
WM_IME_STARTCOMPOSITION | gs_modalEntryWindowCount++ | false | IMEポップアップのカウントアップ | ||
WM_IME_ENDCOMPOSITION | gs_modalEntryWindowCount-- | false | IMEポップアップのカウントダウン | ||
WM_HOTKEY | HandleHotKey(...) | - | |||
WM_CUT など | HandleClipboardEvent(...) | - | |||
WM_HSCROLL など | MSWOnScroll(...) | - | |||
WM_CTLCOLORMSGBOX など | HandleCtlColor(...) | - | |||
WM_SYSCOLORCHANGE | HandleSysColorChange(...) | - | |||
WM_DISPLAYCHANGE | HandleDisplayChange(...) | - | |||
WM_PALETTECHANGED | HandlePaletteChanged(...) | - | |||
WM_CAPTURECHANGED | HandleCaptureChanged(...) | - | |||
WM_SETTINGCHANGE | HandleSettingChange(...) | - | |||
WM_QUERYNEWPALETTE | HandleQueryNewPalette(...) | - | |||
WM_ERASEBKGND | it->second->MSWEraseBgHook(...) | (理解困難)、trueならrc.result=true | |||
WM_DROPFILES | HandleDropFiles(...) | - | |||
WM_INITDIALOG | HandleInitDialog(...) | trueならrc.result=false | |||
WM_QUERYENDSESSION | HandleQueryEndSession(...) | - | |||
WM_ENDSESSION | HandleEndSession(...) | - | |||
WM_GETMINMAXINFO | HandleGetMinMaxInfo(...) | - | |||
WM_SETCURSOR | HandleSetCursor(...) | trueならrc.result=true | |||
WM_GETOBJECT | GetOrCreateAccessible(...) | trueならrc.result=オブジェクト参照 | |||
WM_HELP | ヘルプイベント生成 | true | |||
WM_CONTEXTMENU | コンテキストメニューイベント | true | (理解困難) | ||
WM_MENUCHAR | HandleMenuChar(...)!=wxNOT_FOUND | ポップアップ、rc.result=アイテム | |||
WM_INITMENUPOPUP など | HandleMenuPopup(...) | - | |||
WM_MENUSELECT | HandleMenuSelect(...) | - | |||
WM_POWERBROADCAST | HandlePower(...) | trueで拒絶なければrc.result=true | |||
WM_NCCALCSIZE | NC領域サイズを計算 | true | 先にMSWDefWindowProcをコール | ||
WM_NCPAINT | NC領域を描画 | true | 先にMSWDefWindowProcをコール |