12

WNDPROC非静的クラス メンバーを標準関数にバインドしようとしています。クラスメンバーを静的にすることで、これを簡単に実行できることはわかっています。しかし、C++11 STL の学習者として、<functional>ヘッダーの下にあるツールを使用してそれを行うことに非常に興味があります。

私のコードは次のとおりです。

class MainWindow
{
    public:
        void Create()
        {
            WNDCLASSEXW WindowClass;
            WindowClass.cbSize          = sizeof(WNDCLASSEX);
            WindowClass.style           = m_ClassStyles;
            WindowClass.lpfnWndProc     = std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)>
                                            (   std::bind(&MainWindow::WindowProc, 
                                                *this,
                                                std::placeholders::_1,
                                                std::placeholders::_2,
                                                std::placeholders::_3,
                                                std::placeholders::_4));
            WindowClass.cbClsExtra      = 0;
            WindowClass.cbWndExtra      = 0;
            WindowClass.hInstance       = m_hInstance;
            WindowClass.hIcon           = LoadIconW(m_hInstance, MAKEINTRESOURCEW(IDI_WINDOW));
            WindowClass.hCursor         = LoadCursor(NULL, IDC_ARROW);
            WindowClass.hbrBackground   = (HBRUSH) COLOR_WINDOW;
            WindowClass.lpszMenuName    = MAKEINTRESOURCEW(IDR_MENU);
            WindowClass.lpszClassName   = m_ClassName.c_str();
            WindowClass.hIconSm         = LoadIconW(m_hInstance, MAKEINTRESOURCEW(IDI_WINDOW_SMALL));
            RegisterClassExW(&WindowClass);
            m_hWnd = CreateWindowEx(/*_In_      DWORD*/     ExtendedStyles,
                                    /*_In_opt_  LPCTSTR*/   m_ClassName.c_str(),
                                    /*_In_opt_  LPCTSTR*/   m_WindowTitle.c_str(),
                                    /*_In_      DWORD*/     m_Styles,
                                    /*_In_      int*/       m_x,
                                    /*_In_      int*/       m_y,
                                    /*_In_      int*/       m_Width,
                                    /*_In_      int*/       m_Height,
                                    /*_In_opt_  HWND*/      HWND_DESKTOP,
                                    /*_In_opt_  HMENU*/     NULL,
                                    /*_In_opt_  HINSTANCE*/ WindowClass.hInstance,
                                    /*_In_opt_  LPVOID*/    NULL);

        }

    private:
        LRESULT CALLBACK WindowProc(_In_ HWND hwnd,
                                    _In_ UINT uMsg,
                                    _In_ WPARAM wParam,
                                    _In_ LPARAM lParam)
        {
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
};

そのまま実行すると、次のエラー メッセージが表示されます。

Error: no suitable conversion function from "std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)>" to "WNDPROC".
4

2 に答える 2

13

JohnB はこれが不可能な理由の詳細をすでに説明していますが、解決しようとしている問題に対する一般的な解決策は次のとおりです。静的クラス メンバーへのクラス インスタンス アクセスの許可。

解決策の指針となる原則は、静的クラス メンバーがアクセスできる方法でインスタンス ポインターを格納する必要があるということです。ウィンドウを扱う場合、余分なウィンドウ メモリは、この情報を格納するのに適した場所です。追加のウィンドウ メモリの要求されたスペースは によって指定されWNDCLASSEXW::cbWndExtra、データ アクセスは および によって提供されSetWindowLongPtrますGetWindowLongPtr

  1. 構築後にウィンドウの追加データ領域にインスタンス ポインタを格納します。

    void Create()
    {
        WNDCLASSEXW WindowClass;
        // ...
        // Assign the static WindowProc
        WindowClass.lpfnWndProc = &MainWindow::StaticWindowProc;
        // Reserve space to store the instance pointer
        WindowClass.cbWndExtra  = sizeof(MainWindow*);
        // ...
        RegisterClassExW(&WindowClass);
        m_hWnd = CreateWindowEx( /* ... */ );
    
        // Store instance pointer
        SetWindowLongPtrW(m_hWnd, 0, reinterpret_cast<LONG_PTR>(this));
    }
    
  2. 静的ウィンドウ プロシージャからインスタンス ポインターを取得し、ウィンドウ プロシージャ メンバー関数を呼び出します。

    static LRESULT CALLBACK StaticWindowProc( _In_ HWND hwnd,
                                              _In_ UINT uMsg,
                                              _In_ WPARAM wParam,
                                              _In_ LPARAM lParam )
    {
        // Retrieve instance pointer
        MainWindow* pWnd = reinterpret_cast<MainWindow*>(GetWindowLongPtrW(hwnd, 0));
        if ( pWnd != NULL )  // See Note 1 below
            // Call member function if instance is available
            return pWnd->WindowProc(hwnd, uMsg, wParam, lParam);
        else
            // Otherwise perform default message handling
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    

    クラス メンバーの署名は、指定WindowProcしたコードと同じです。

これは、目的の動作を実装する 1 つの方法です。Remy Lebeau は、クラス member を介してルーティングされるすべてのメッセージを取得するという利点がある、これに対するバリエーションを提案しましたWindowProc

  1. ウィンドウの追加データにスペースを割り当てます (上記と同じ):

    void Create()
    {
        WNDCLASSEXW WindowClass;
        // ...
        // Assign the static WindowProc
        WindowClass.lpfnWndProc = &MainWindow::StaticWindowProc;
        // Reserve space to store the instance pointer
        WindowClass.cbWndExtra  = sizeof(MainWindow*);
        // ...
    
  2. インスタンス ポインタを に渡しますCreateWindowExW

        m_hWnd = CreateWindowEx( /* ... */,
                                 static_cast<LPVOID>(this) );
        // SetWindowLongPtrW is called from the message handler
    }
    
  3. WM_NCCREATE最初のメッセージ ( ) がウィンドウに送信されたときに、インスタンス ポインターを抽出し、ウィンドウの追加データ領域に格納します。

    static LRESULT CALLBACK StaticWindowProc( _In_ HWND hwnd,
                                              _In_ UINT uMsg,
                                              _In_ WPARAM wParam,
                                              _In_ LPARAM lParam )
    {
        // Store instance pointer while handling the first message
        if ( uMsg == WM_NCCREATE )
        {
            CREATESTRUCT* pCS = reinterpret_cast<CREATESTRUCT*>(lParam);
            LPVOID pThis = pCS->lpCreateParams;
            SetWindowLongPtrW(hwnd, 0, reinterpret_cast<LONG_PTR>(pThis));
        }
    
        // At this point the instance pointer will always be available
        MainWindow* pWnd = reinterpret_cast<MainWindow*>(GetWindowLongPtrW(hwnd, 0));
        // see Note 1a below
        return pWnd->WindowProc(hwnd, uMsg, wParam, lParam);
    }
    

lpfnWndProc注 1: インスタンス ポインタは、ウィンドウが作成される前に設定されている間、ウィンドウが作成された後にウィンドウ エクストラ データ領域に格納されます。これはStaticWindowProc、インスタンス ポインタがまだ利用できない間に呼び出されることを意味します。結果として、作成中のメッセージ ( など) が適切に処理されるように、if内部に -statementが必要です。StaticWindowProcWM_CREATE

注 1a: 注 1 に記載されている制限は、代替実装には適用されません。インスタンス ポインターは、最初のメッセージから先に使用できるようになり、WindowProcその結果、すべてのメッセージに対してクラス メンバーが呼び出されます。

注 2: 基になるインスタンスHWNDが破棄されたときに C++ クラス インスタンスを破棄する場合WM_NCDESTROYは、ここで行います。これは、任意のウィンドウに送信される最終メッセージです。

于 2013-08-10T14:43:00.387 に答える