ちいつもお世話になっております。Win32APIにおけるコントロールのクラス化についてお聞きしたいと思い、書き込みを行いました。index.htmlにある、「一般的なWindowsプログラミング」の「プロシージャのメンバ化」から「サブクラス化」までを参考に実現しようと考えていました。ところが「サブクラス化」において、SetWindowLongの第3引数に指定したウィンドウプロシージャはどのような形で持たせておくべきなのかわからずに詰ってしまいました。コントロールのクラス化というと、そのウィンドウプロシージャもメソッドとして持っておくべきなのではないかと思ったのですが、これはstaticでなくてはなりません。//ここで、SetWindowLongを呼び出し、ウィンドウプロシージャを「MainProc」から「ControlProc」に変更。←もちろんこれはControlのインスタンスが作成されていないためエラーとなる。//コントロール別のプロシージャ};のように行いたいです。このままではSetWindowLongでエラーが出るのは確実です。このホームページでは、コントロールのクラス化について可能であるようなことが書いてあるので、どのように実現するのか知りたかったので質問しました。このホームページの管理人さんに連絡が取れればよかったのですが、最終更新日が2005年で、BBSも消えてしまっている状態なので、場違いかと思ったのですがここで質問させていただきました。ISLeそのページに書いてあるとおりにすれば良いと思うのですが。おっしゃるとおりWin32APIで指定するウインドウプロシージャは静的なメンバ関数である必要があります。静的ではないメンバ関数では引数とともに暗黙にインスタンスへのポインタを渡すので関数の呼び出し方が違うからです。取得したインスターンスへのポインタを使って静的ではないメンバ関数で用意したウインドウプロシージャを呼び出すという手順を踏む必要があります。「サブクラス化」のページではウインドウプロシージャをフックする方法しか書かれていませんので、静的ではないメンバ関数で用意したウインドウプロシージャを呼び出すためには同時にウインドウのユーザー用メモリ領域にインスタンスへのポインタ(this)を記録する必要があります。サブクラス化する対象のウインドウによっては既にウインドウのユーザー用メモリ領域が既に別の目的に使われている可能性がありますので、元のウインドウプロシージャを呼び出す前にウインドウのユーザー用メモリ領域を元の値に戻し、元のウインドウプロシージャから戻ったらthisポインタを書き込む処理を追加する必要があります。ちISLeさん返信ありがとうございます。すいません、私の理解不足のためか、やはりよく流れがわかっていないようです。1)のユーザー用メモリ領域にインスタンスへのポインタを記録するというのはわかるのですが、その後の流れがいまいちよくわかりません。ウィンドウプロシージャをフックする方法というのは、基本的にはどこで呼ぶべきものなのでしょう?前の節で作成した静的なメンバ関数に処理を追加するということでしょうか?だとすると、コントロールとウィンドウのプロシージャをひとまとめにするってことでしょうか?完全に理解不足なため、うまく言葉に出来なくて申し訳ないですが、よろしくお願いします。ウィンドウプロシージャをフックする方法というのは、基本的にはどこで呼ぶべきものなのでしょう?ウインドウハンドルを使うのでウインドウハンドルが取得できるタイミングならいつでも良いです。質問にあるコントロールとは自作のコントロールなのでしょうか?「サブクラス化」は既存のコントールでソースコードから手を加えられない(加えたくない)ときにしか必要ないです。自作のコントロールならそのままのコードを流用できるはずです。MainProcをControlProcに変えたいだけなら「ウィンドウのクラス化」ページのCBaseWnd::CallProcにMainProcを呼び出しているところがあります。APIの最後の引数を経由して受け取っていますが、WM_CREATEメッセージを処理するコードの中でインスタンスを作成しても構いません。APIの呼び出しが失敗したことになります。すると、コントロールとウィンドウのプロシージャをひとまとめにするってことでしょうか?この意味が分かりません。コントロールとウインドウで何が違うのでしょう?もしかしてサンプルどおりだとMainProcしか呼ばれないがControlProcも呼び出すようにしたいということでしょうか。MainProcの最初にControlProcを呼び出すようにオーバーライドするだけです。返信ありがとうございます。質問にあるコントロールとは自作のコントロールなのでしょうか?はい、もちろん自作です。コントロールとウインドウで何が違うのでしょう?コントロールとウィンドウは基本的には同じものとして考えています。うーん、なんて説明したらよいかわからないのですが・・・。メッセージを横取りする方法という点がコントロールと違うのかなと思ったのですが・・・。なんとなく、ISLeさんのおっしゃっていることがわかってきた気がします。はい、もちろん自作です。既存のコントロールに似たものでなく標準のコントロールの振る舞いをサブクラス化して継承する必要もないのであればふつうのウインドウと同じように実装すれば良いです。例としてボタンが押されたと判定する時にWM_RBUTTONUPなどで判定すればよいということになるのでしょうか?そもそも標準のコントロールをサブクラス化しない限りBM_~といったコントロール独自のメッセージは飛んできませんのでいわゆるふつうのウインドウメッセージだけで対応する必要があります。コントロール独自のメッセージは標準のコントロール用にあらかじめ用意されているウインドウプロシージャによってコントロール自身が発行しているものです。APIの引数でウインドウクラスに"BUTTON"という文字列を指定するとウインドウズにあらかじめ用意されているボタンコントロール用のウインドウプロシージャが呼ばれるといった仕組みになっています。"BUTTON"というウインドウクラスを使うとき自前のウインドウプロシージャを指定することはできないのでサブクラス化が必要になるわけです。すると、SetWindowLongやCallWindowLongが必要ない気がするのですがこれでもいいのでしょうか?サブクラス化しなければCallWindowProcは必要ないですが、コントロールのプログラムコードをC++のクラスとして記述するならSetWindowLongは必要ではないでしょうか。返信ありがとうございます。コントロール独自のメッセージは標準のコントロール用にあらかじめ用意されているウインドウプロシージャによってコントロール自身が発行しているものです。APIの引数でウインドウクラスに"BUTTON"という文字列を指定するとウインドウズにあらかじめ用意されているボタンコントロール用のウインドウプロシージャが呼ばれるといった仕組みになっています。"BUTTON"というウインドウクラスを使うとき自前のウインドウプロシージャを指定することはできないのでサブクラス化が必要になるわけです。したら自作のコントロールっていうのは"BUTTON"を指定しない場合に作った時のことですか?それだったら、今回の場合は"BUTTON"などを指定しているため自作ではないということになってしまうのでしょうか?勘違いしていたようで申し訳ありません。"BUTTON"のように指定すると、サブクラス化しないとダメですかね?以下はサブクラス化の実装手段についての質問です。今のところ、MainProcをコントロールが持っている状態で、サブクラス化したときに作るべきウィンドウプロシージャをどのようにしたらよいのかが、さっぱりつかめません。サブクラス化しなければCallWindowProcは必要ないですが、コントロールのプログラムコードをC++のクラスとして記述するならSetWindowLongは必要ではないでしょうか。あ、確かにコントロールでもSetWindowLongは必要でしたね。すいません、間違えました。"BUTTON"のように指定すると、サブクラス化しないとダメですかね?標準のボタンコントロールの振る舞いを継承するなら作成されたウインドウハンドルを使ってサブクラス化する必要がありますね。以下はサブクラス化の実装手段についての質問です。今のところ、MainProcをコントロールが持っている状態で、サブクラス化したときに作るべきウィンドウプロシージャをどのようにしたらよいのかが、さっぱりつかめません。「ウィンドウのクラス化」ページで言うところのCBaseWnd::CallProcをセットすれば良いです。MainProc用の静的メソッド(ホームページで言うCallProc)と、ControlProc用の静的メソッドが両方必要ということでしょうか?MainProcのオーバーライドだけで目的を達成することは可能だと思います。MainProcは仮想関数なのでControlクラスでオーバーライドした関数が呼ばれます。ControlクラスにControlProcを呼び出す静的なメンバ関数を定義するかどうかは設計の問題なので必要・不必要のどちらとも言えません。返信ありがとうございます。考えていただけでは、進まないので試行錯誤してやってみました。ISLeさんの提案による、MainProcのオーバーライドの方法で行ってみました。コントロール上でクリックすると、メッセージボックスが表示されるというものです。反応していないようです。そもそも、デバッグを掛けてみると、オーバーライドしたもののウィンドウプロシージャが呼ばれていないみたいです。staticなウィンドウプロシージャ入れ替え用のメソッドを作ること以外で、CBaseWndクラスを継承してサブクラス化を行うというのはやはり出来ないのでしょうか、うーん。一応、参考までに必要な部分のソースコードを載せてみますので、間違い名部分があるようでしたら指摘していただければ助かります。ISLeソースコードはコンパイルできませんでしたが眺めてみました。defWndProc;を追加。(LONG)(Window::MsgProc));SetPointer(hWnd);の二行を追加。これでMyControl::WndProcが呼ばれるのではないでしょうか。継承しているのですからMyControl::WndProcはフックしたメッセージ以外はControl::WndProcを呼び出して投げてしまうほうが良いと思います。あとは適当に整理してください。・コントロールのウィンドウを作成すると、コントロールのウィンドウプロシージャが自動的に作成してしまう。・自動的に作成されたウィンドウプロシージャをWindow::MsgProcという静的メンバ関数に置き換える。・こうすることで、コントロールが自動生成したウィンドウプロシージャに対するメッセージをWindow::MsgProcが横取りする。・Window::MsgProcでは、それぞれのウィンドウのクラスで独自に作成した静的でないウィンドウプロシージャを呼び出すことになる。・結果として、Control::WndProcが呼び出される。・Control::WndProcが呼び出された後、場合によりCallWindowProcによりコントロールが自動的に作成したウィンドウプロシージャを実行する。・SetPointerをわざわざ呼び出しているのは、Window::MsgProcを呼び出したときにウィンドウプロシージャが受け取るHWNDは、親ウィンドウのものであるため、Window::MsgProc内のSetPointerの部分が呼び出されないため。質問に答えていただきありがとうございました。・結果として、Control::WndProcが呼び出される。「結果として」というところに、質問に書かれたリンク先のページで言うところの「プロシージャのメンバ化」「ウインドウのクラス化」で説明されている仕組みがあるわけですがずいぶん省略してしまいましたね。・SetPointerをわざわざ呼び出しているのは、Window::MsgProcを呼び出したときにウィンドウプロシージャが受け取るHWNDは、親ウィンドウのものであるため、Window::MsgProc内のSetPointerの部分が呼び出されないため。No:65540では間違えてますがControl::Create関数に追加したコードにm_HWndを使ったと思います。m_HWndはボタンコントールのウインドウハンドルなので親ウインドウではないです。SetPointerの中身を見てもらえればウインドウのユーザー用メモリ領域にthisポインタを記録しているのが分かると思いますが、これはWindow::MsgProcで静的ではないメンバ関数を呼び出すためです。「プロシージャのメンバ化」「ウインドウのクラス化」で説明されているものと同じです。ウインドウのユーザー用メモリ領域にインスタンスへのポインタを記録したらいったんこちらの手を離れて、ウインドウズがウインドウプロシージャとして呼び出した静的なメンバ関数でウインドウのユーザー用メモリ領域からインスタンスへのポインタを取得してメンバ関数を呼び出すという一連の動作を理解されていないように見受けられます。静的なメンバ関数がウインドウプロシージャとして呼び出されるところは時間的な隔たりがあります。インスタンスやポインタについて知識や経験を深めた上で実行時の様子を想像しながらコードを追いかけてください。コードを順番に読み進めるだけでは分からないと思います。クラスを有効に活用できるようMyControlを複数配置したときのプログラムの動きを追いかけられるように頑張ってみてください。ちISLeさん、最後まで長々と私の質問に付き合っていただきありがとうございます。プログラミングの経験が浅いですが、これからも続けて経験を深めていきたいと思います。Limitedhttps://www.phpbb.com/。