ダイアログボックスをクラス化するさいプロシージャをメンバ化するには?

解決


後藤  2010-12-10 13:40:49  No: 72127  IP: [192.*.*.*]

はじめまして、こんにちは。
開発環境は
Windows Vista で Visual Studio 2008 を使い VC++ で記述しています。
知識は独学でネットの文献を参考にプログラミングをしている程度です。

本題ですが、現在ダイアログボックスをクラス化したいと思い
ダイアログプロシージャをメンバ化する所で詰まっています。

調べたところ
1.静的でないとプロシージャをメンバ化できない。
2.サブクラス化すると可能である。
3.SetWindowLong関数を使うとよい。

等が見つかったので
MyDialog.h
class MyDialog{
  static BOOL CALLBACK MyProc(HWND・・・){
    switch()
     メインの処理の内容
    }
  }
}

DefProc.hに
BOOL CALLBACK DefaultProc(HWND・・・){
  switch(){
    その他の処理
  }
}

としたいと考えています。

どうかご教授のほどよろしくお願い申し上げます。

編集 削除
後藤  2010-12-10 13:44:41  No: 72128  IP: [192.*.*.*]

申し訳ありません。
追記します。

開発環境に、MFC等は使用していません。
APIだけでの開発になりますので
よろしくお願いします。

編集 削除
gak  2010-12-10 17:52:31  No: 72129  IP: [192.*.*.*]

後藤さんが思い描いてる像が今一汲み取れなかったので、俺ならこうするかな?という例。
処理は *1 -> *5 の流れで進むつもり。コンパイル通したコードじゃないんで参考程度に。

class SuperDialog {
    const UINT id_;
    HWND wnd_;

    SuperDialog(UINT id) : id_(id), wnd_(NULL) {}

    INT_PTR ShowDialog(HWND parent) {
        return ::DialogBoxParam(instance, MAKEINTRESOURCE(id), parent, DefaultProc, this); // *2
    };
    static INT_PTR CALLBACK DefaultProc(wnd, msg, w, l) {
        SuperDialog* dlg = (SuperDialog*)::GetWindowLongPtr(wnd, GWLP_USERDATA);
        if (dlg != NULL) {
            return dlg->MyProc(msg, w, l); // *4
        }
        else if (msg == WM_NCCREATE) { // *3(dialog って WM_NCCREATE 来たっけ?ダメな場合は WM_INITDIALOG 辺りで)
            dlg = (SuperDialog*)((CREATESTRUCT*)l)->lpCreateParams;
            ::SetWindowLongPtr(wnd, GWLP_USERDATA, dlg);
            dlg->wnd_ = wnd;
        }
        return FALSE;
    }
    virtual INT_PTR CALLBACK MyProc(msg, w, l) = 0;
};

class MyDialog : public SuperDialog {
    MyDialog() : SuperDialog(IDD_MYDIALOG) {}

    INT_PTR CALLBACK MyProc(msg, w, l) { // *5
        switch (msg) {
            // 以下に処理したいメッセージを追加してく
            case ...:
                return TRUE;
        }
        return FALSE;
    }
};

void xxxxxxxx(HWND parent) {
    MyDialog dlg;
    dlg.ShowDialog(parent); // *1
}

編集 削除
仲澤@失業者  2010-12-13 18:29:39  No: 72130  IP: [192.*.*.*]

ウインドウ(ダイアログ)コールバックをメンバにするのに
クラスのstatic関数を指定するのはよくやる方法ですね。
ただし、厳密に言うとコールバックはC関数であるという要請が
あるので、正しくはC関数として実装し、friend指定する方が、
より良いかもしれません。
いづれにしても、では、どうやってHWND毎に存在するDLGクラス
インスタンスを特定するのか、という問題が残ります。
これは、色んな方法が考えられますが、

1.コールバックが参照できるHWND/DLGクラスインスタンスの一覧表
  を使用する。
2.インスタンスをSetWindowLongPtr()でDWLP_USER位置に埋め込む。

等が考えられますね。

編集 削除
後藤  2010-12-17 09:32:25  No: 72131  IP: [192.*.*.*]

gak様,中澤@失業者様
ご返答ありがとうございます。

>>思い描いてる像
プロシージャをクラス化することで
class Dialog{
 int A,B;
 MyProc(wnd,msg,w,l){
  switch(msg){
   case A:
    A = ***;
    break;
   case B:
    B = +++;
    break;
  }
 return FALSE;
}
のようにメンバ変数に値をいれることで、グローバル変数を使用
せず、わかりやすいコードにしたいと思ったからです。

>>HWND毎に存在するDLGクラスインスタンスの特定
おっしゃる通り2番のSetWindowLongPtr()を使用したいと思っています。

ご提示頂いたソースを自分なりに実装してみたのですが
wndの中身がエラーになってしまい、色々試したのですが
うまくいきません。

原因として考えられるのは
現在のプログラムは
ウィンドウが存在し、ウィンドウプロシージャがクラス化されています
そこからダイアログボックスを呼び出しているので
エラーになってしまうのでしょうか・・・

編集 削除
仲澤@失業者  2010-12-17 09:50:33  No: 72132  IP: [192.*.*.*]

大昔に書いた、自分のDLGクラスは次のようになってましたので、
参考にしてください。

1.全DLG共用のコールバックをC関数で作る。
2.DLG(ウインドウの作成)は当該クラス(Dialog)のメンバ関数で作成し
  DialogBoxParam()を使う。このときLPARAM dwInitParam に
  当該クラス(Dialog)のthisを渡す。
  当然、DLG共用のコールバックはfriendにしてある。
3.DLG共用のコールバックで、WM_INITDIALOGを受領したとき、
  引数のLPARAMにさっきのthis(=Dialog*)が渡ってくる。
4.ので、クラスインスタンスポインタ(=Dialog*)にLPARAMをキャストして、
  SetWindowLongPtr()で、DWLP_USER位置にクラスポインタ(=Dialog*)
  を埋め込む。
5.WM_INITDIALOG以外のメッセージは、GetWindowLongPtr()でDWLP_USER位置
  から、クラスポインタ(=Dialog*)を取得してそれの->MyProc()を
  コールして、戻り値をリターンする。
6.当然MyProc()では、不要な処理はDefdlgProc()で処理する。

編集 削除
後藤  2010-12-17 11:23:02  No: 72133  IP: [192.*.*.*]

仲澤様
解答ありがとうございます。

>>1
BOOL CALLBACK DefdlgProc() のことでしょうか

>>2
DialogBoxParam(親ウィンドウInstance,リソースID,
              親ウィンドウハンドル,dlgProc,(LPARAM)this);
ということにしました。

>>4
dlg = (Dialog*)((CREATESTRUCT*)lPalam)->lpCreateParams;

5番6番をどのように実装したらいいのか・・・申し訳ありませんが
ソースでのご教授をお願いいたします。

編集 削除
仲澤@失業者  2010-12-17 12:30:01  No: 72134  IP: [192.*.*.*]

>>1
>BOOL CALLBACK DefdlgProc() のことでしょうか
DialogBoxParam()に渡すダイアログボックスプロシージャです。
関数名は何でもよいです。

>>4
>dlg = (Dialog*)((CREATESTRUCT*)lPalam)->lpCreateParams;
仮に当該クラスをclass XXDLGとすると
WM_INITDIALOG でのlParamはXXDLGのインスタンスのポインタの
はずなので、XXDLG * xxDlg = ( XXDLG *)lParam;のはず。
CREATESTRUCTはMFCが使ってるやつですね。つまりMFCも似た様な
仕組みを利用しているわけです。

>5番6番をどのように実装したらいいのか・・・申し訳ありませんが
>ソースでのご教授をお願いいたします。
ダイアログボックスプロシージャをCOM_DLG_Proc()とすると
BOOL COM_DLG_Proc( HWND h, UINT m, WPARAM w, LPARAM l)
{
  XXDLG * xxDlg;
  if( WM_INITDIALOG == m){
     xxDlg = ( XXDLG *)lParam;
     ::SetWindowLongPtr( h, DWLP_USER, xxDlg);
     xxDlg->HWND_Set( h); // メンバHWNDの設定
  }
  else{
     xxDlg = ( XXDLG *)::GetWindowLongPtr( h, DWLP_USER);
  }
  return xxDlg->INST_CallBack( m, w, l);
}
てな感じ、コンパイルしてないのでよろしく。

編集 削除
後藤  2011-01-14 11:16:08  No: 72135  IP: [192.*.*.*]

返信が遅くなり申し訳ございません。
色々試したのですがうまくいかず
納期が迫ってしまったのであきらめました。

色々ご教授ありがとうございました。

編集 削除