1

クラスのメンバーであるダイアログ プロシージャを使用するにはどうすればよいでしょうか。ウィンドウ ラッパー クラスを作成していCreateDialogParamますが、グローバル ダイアログ プロシージャが必要なので、次の回避策を試しました。

このトピックについて少し検索しました。私DialogはサブクラスCMainWnd化してインスタンス化するクラスを作成しています。クラスには、Dialogとして定義されたメンバー関数がありますINT_PTR CALLBACK Dialog::cb_proc(HWND,UINT,WPARAM,LPARAM)。これで、ウィンドウにはコールバック プロシージャとしてグローバル関数が必要であることがわかりました。

そこでstd::map<HWND,Dialog*> DlgProcs、ダイアログ ウィンドウ ハンドルをその Dialog クラス ポインタに関連付けるマップを作成しました。

そして、INT_PTR CALLBACK DlgMainProc(HWND,UINT,WPARAM,LPARAM)私はそれをに渡すことができましたCreateDialogParam()。の本体で、パラメーターをDlgMainProc(...)使用してマップを検索し、そのメンバーを見つけて返します。hWndDialog*cb_proc(..)

私の問題は、メッセージが処理されないことです。これは、Dialogクラスのメンバープロシージャが呼び出されないためです。MessageBox()ステートメントのDlgMainProc中に aを入れるif (DlgProcs.find(hWnd) != DlgProcs.end()) {と、Visual Studio 2008 からプログラムを中止しなければならなくなるまで、何度も何度もメッセージ ボックスが表示されhWndます。奇妙なことは、その後のステートメントにそれを入れると、これも実行されることです。これは、マップでelseが見つからないことを矛盾して伝えています。hWnd

メンバー関数にメッセージ ボックスを配置すると、cb_procまったく表示されません。しかし、この間、コンパイラ、リンカー、または実行時エラーが発生することはありません。そこからメッセージボックスを削除すると (プログラムを中止する必要がないため、デバッグ目的のためだけでした)、プログラムは実行されますが、メッセージは処理されず、X ボタンはプログラムを閉じず、ボタンのクリックは何もしません。


PasteBin コードは次のとおりです: http://pastebin.com/GsGUBpZU ところで、これをサブクラス化しても問題ありません。ウィンドウは正常に作成され、メッセージは処理されず、cb_proc呼び出されません。

編集:コードの関連部分は次のとおりです

map<HWND,Dialog*> g_DlgProcs;

INT_PTR CALLBACK g_MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        if (g_DlgProcs.find(hWnd) != g_DlgProcs.end()) {
                Alert("blah"); // Gets executed repeatedly
                return g_DlgProcs[hWnd]->cb_proc(hWnd, msg, wParam, lParam);
        } else {
                Alert("blah"); // Removing the above alert, this gets
                               // executed repeatedly, erm, as well.. O.o strange
                return FALSE;
        }
}

Dialog::Dialog(int id, HWND parent /* = HWND_DESKTOP */) {
        _id = id;
        _parent = parent;

        // Tried this before CreateDialogParam
        g_DlgProcs.insert(make_pair(_handle, this));

        _handle = CreateDialogParam(
                (HINSTANCE)GetModuleHandle(NULL),
                MAKEINTRESOURCE(id), _parent,
                (DLGPROC)g_MainDlgProc, NULL
        );

        // Then tried it after CreateDialogParam
        g_DlgProcs.insert(make_pair(_handle, this));
}

INT_PTR CALLBACK Dialog::cb_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        Alert("blah"); // Never gets executed

        bool handled = true;

        switch (msg)
        {
        case WM_INITDIALOG:
                OnInitialize();
                break;
        case WM_COMMAND:
                if (HIWORD(wParam) == 0 || HIWORD(wParam) == 1) {
                        OnMenuCommand((HIWORD(wParam) == 1), (int)LOWORD(wParam));
                } else {
                        OnCtrlCommand((int)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam);
                }
                break;
        case WM_NOTIFY:
                {
                        LPNMHDR head = (LPNMHDR)lParam;
                        OnNotification(head->code, head->idFrom, head->hwndFrom);
                }
                break;
        case WM_CLOSE:
                OnClose(); // DestroyWindow(_handle)
                break;
        case WM_DESTROY:
                OnDestroy(); // PostQuitMessage(0)
        default:
                handled = ProcessMsg(msg, wParam, lParam);
        }

        // Convert bool to Windows BOOL enum
        return ((handled == true) ? TRUE : FALSE);
}

なぜ呼び出されないのか誰か知っていますか?または、メンバー関数を DLGPROC として使用する別の方法を教えてください。

4

3 に答える 3

2

私はあなたのコードを試してみましたが、うまくいきました:cb_proc呼び出されます。WM_INITDIALOG戻る前に送信されたメッセージ (例: ) を見逃すことになりCreateDialogParamます。

後者の問題は、ウィンドウ ハンドルとオブジェクトを のマップに追加することで解決できますg_MainDlgProc。不明なウィンドウに関するメッセージが表示された場合、それが作成中のウィンドウに属していることがわかります。オブジェクトをグローバルに配置すると、ハンドル/オブジェクトをマップに追加できます。

于 2012-09-25T12:29:10.027 に答える
0

誰かが役に立つと思った場合に備えて、ここに追加しています。C++11 ラムダとテンプレートの魔法を使用すると、単純なラッパー テンプレートを作成できます。つまり、ウィンドウとダイアログ ボックスの手順でユーザーデータを保存およびロードするための定型コードを継続的に書き直す必要はありません。

関数の例を次に示しDialogBoxParamますが、同じ手法をCreateDialogParamandCreateWindowExにも適用できます。

template <typename T, INT_PTR (T::*P)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)>
INT_PTR DialogBoxThis(T* pThis, HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hWndParent)
{
    return ::DialogBoxParam(hInstance, lpTemplateName, hWndParent, [](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> INT_PTR {
        if (uMsg == WM_INITDIALOG) SetWindowLongPtr(hWnd, DWLP_USER, lParam);
        T* pThis = reinterpret_cast<T*>(GetWindowLongPtr(hWnd, DWLP_USER));
        return pThis ? (pThis->*P)(hWnd, uMsg, wParam, lParam) : FALSE;
    }, reinterpret_cast<LPARAM>(pThis));
}

次のように使用します。

class MyClass
{
    INT_PTR MyDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};

// from inside MyClass, we can show a dialog that uses member function MyDlgProc as the dialog procedure
// note it is NOT a static function

DialogBoxThis<MyClass, &MyClass::MyDlgProc>(this, hInstance,
    MAKEINTRESOURCE(IDD_MYDIALOG), hWndParent);
于 2015-07-21T23:53:58.633 に答える