(BCCSkeltonと似たか寄ったかで)「めんどーくせーなー」、と始めたECCSkeltonですが、色々とトラブルがあり、トラブルシューティングに嵌って結構時間つぶしができています。
<トラブルその1-純粋仮想関数>
C++でクラスを作る際に、"virtual"を付けたメンバー関数の宣言だけして"= 0;"を付けると純粋仮想関数となり、派生クラスで定義をすればよいのはご高尚の通りです。20年離れていたので、純粋仮想関数は放っておけば眠ったままか、と思ったのですが、派生クラスで定義しないと「〇〇は抽象基底クラスで、関数の定義がされていません。」というお叱りを受けます。まぁ、これも派生クラスで純粋仮想関数を維持すればよいのでしょうが、BCCSkeltonでカバーしている主要WM_メッセージだけでもいっぱいあるので毎回ズラズラ書くのもしんどく、純粋仮想関数は断念して単純にオーバーライドが効くデフォールトの仮想関数にしてズラッと並べてみました。(注)
注:c++11から、オーバーライドする派生クラスの関数には明示的に"override"キーワードを付けられるようです。
例:(基底クラス)virtual bool foo() {such and such};
(派生クラス)bool foo() {so and so} override;
みたいな感じです。また、派生先でオーバーライドを禁止する"final"キーワードも付けられるようです。更に
「以前は仮想関数であることを示すために派生クラスでも virtual を書く場合がありましたが、
現在では override または final を使い、virtual は使いません。」
だそうです。フ~ン、色々と変わってきているんですね。(因みに現在の段階ではまだ"override"を使っていません。)
<トラブルその2-CDLGクラスインスタンスの"this"の引き渡し>
BCCSkeltonではCWNDやCDLGや派生クラスのコールバック関数を外部関数にしていました。(速いし、コンパクトだから。)
ECCSkeltonではクラスの静的関数からインスタンス(ウィンドウ)別のメンバーコールバック関数を呼ぶ形にしてみたのですが、モーダルダイアログのDialogBoxなどではウィンドウハンドルが戻り値で帰ってこないのでウィンドウのプロパティにクラスインスタンスの"this"を書き込めません。
これは悩んだのですが、DialogBoxParamという良い関数があり、WM_INITDIALOGのlParamに"this"を与えることで解決しました。これはモードレスダイアログでもCreateDialogParamで同様に採用しました。(注)
注: if(Message == WM_INITDIALOG) {
//ウィンドウハンドルを取得し、
((CDLG*)lParam)->m_hWnd = hWnd;
//ウィンドウプロパティにCDLGクラスインスタンスへのポインターを書込む(重要)
SetPropW(hWnd, L"CPP_CLASS", (HANDLE)lParam);
}
<トラブルその3-WM_CREATEでステータスバーやEDITコントロールが作れない?>
CSDIのテストプログラムで、WM_CREATEに対応するOnCreate(WPARAM, LPARAM)関数でステータスバーやEDITコントロールを作ろうとしたのですが、エラーになります。原因が分からず、
「なんでエラーになるんじゃいっ!!!」
と切れましたが、今朝早朝「あっ、WM_CREATEが終わるまでCreateWindowExW関数の戻り値が返されないなら、OnCreate関数の中ではm_hWndは'0(ゼロ)'のままかも?」というひらめきが降りてきて、↑のトラブルその2の応用で「CreateWindowExW関数の"PVOID pParam"引数に"this"を与えればいいじゃん!」ということでやってみましたがうまく動きません。「ん?」ということで、与える時の"this"の値と、WM_CREATEのlParamの値を比べると全く別物!
という訳で後からMSN Docを読む(笑・泣;)と「WM_CREATEの際のlParamはCREATESTRUCTW構造体へのポインタ」であり、pParamの値はこの構造体のLPVOID lpCreateParamsメンバーに入っているそーな。という訳でCDLGの時の作法をそのまま移植して、
if(Message == WM_CREATE) { //最初のWM_CREATEで、
//m_hWndを取得し(これをしないとOnCreateで失敗する)、
((CSDI*)((CREATESTRUCT*)lParam)->lpCreateParams)->m_hWnd = hWnd;
//クラスインスタンスポインターを受け取りウィンドウプロパティに書込む(重要)
SetPropW(hWnd, L"CPP_CLASS", (HANDLE)((CREATESTRUCT*)lParam)->lpCreateParams);
}
というキャスト、キャストの「寿限無、寿限無」的記法で解決し、きちんとステータスバー、EDITコントロールが表示できました。
<今後どうする?>
何か、ECCSkeltonのテスト用に書いてきたCSDITest.cppというプログラムがBCCSkeltonWplusのWCEditorに酷似してきたので、ECCSkeltonのコマンドマクロ(注)を書きながら、テストプログラムとしてWCEditorのECCSkelton版にしてみようかな、と考えています。BCCSkeltonとの互換性もあるし、BCCSkeltonWplusで書いたクラスヘッダーもし利用できそうです。
注:BCCSkeltonではコールバック関数を外部関数として、それをマクロテーブルにしましたが、ECCSkeltonではメンバー仮想関数のOnCommand(WPARAM, LPARAM)関数の中でコントロールIDに応じた"Onなんちゃら()"関数を呼ぶので、そこの部分はマクロ化しようかなと考えています。