我如何`std :: bind`一个非静态类成员到Win32回调函数`WNDPROC`? [英] How do I `std::bind` a non-static class member to a Win32 callback function `WNDPROC`?

查看:258
本文介绍了我如何`std :: bind`一个非静态类成员到Win32回调函数`WNDPROC`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试将非静态类成员绑定到标准 WNDPROC 函数。我知道我可以简单地这样做通过使类成员静态。但是,作为一个C ++ 11 STL学习者,我对使用< functional> 头下的工具非常感兴趣。

I'm trying to bind a non-static class member to a standard WNDPROC function. I know I can simply do this by making the class member static. But, as a C++11 STL learner, I'm very interested in doing it by using the tools under the <functional> header.

我的代码如下。

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);
        }
};

当我按原样运行时,会显示错误信息:

When I run it as is, it gives the error message:

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


推荐答案

虽然JohnB已经解释了为什么不可能,这里是一个常见的解决方案,你要解决的问题:授予类实例访问静态类成员。

While JohnB already explained the details why this is not possible, here is a common solution to the problem you are trying to solve: Granting class instance access to a static class member.

解决方案的指导原则是,指针必须以静态类成员可访问的方式存储。当处理窗口时,额外的窗口存储器是存储此信息的好地方。额外窗口存储器的请求空间通过 WNDCLASSEXW :: cbWndExtra 指定,而数据访问通过 SetWindowLongPtr GetWindowLongPtr

The guiding principle to the solution is that an instance pointer must be stored in a way that is accessible to the static class member. When dealing with windows the extra window memory is a good place to store this information. The requested space of extra window memory is specified through WNDCLASSEXW::cbWndExtra while data access is provided through SetWindowLongPtr and GetWindowLongPtr.


  1. 在建构之后,在窗口额外数据区域中存储实例指针:

  1. Store an instance pointer in the window extra data area after construction:

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));
}


  • 从静态窗口过程检索实例指针,窗口过程成员函数:

  • Retrieve the instance pointer from the static window procedure and call into the window procedure member function:

    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 与您提供的代码相同。

    The signature of the class member WindowProc is the same as in the code you provided.

    这是实现所需行为的一种方法。 Remy Lebeau建议了一个变体,它有利于通过类成员 WindowProc 路由所有消息:

    This is one way to implement the desired behavior. Remy Lebeau suggested a variation to this which has the benefit of getting all messages routed through the class member WindowProc:


    1. 在窗口中分配空格额外数据(与上述相同):

    1. Allocate space in the window extra data (same as above):

    void Create()
    {
        WNDCLASSEXW WindowClass;
        // ...
        // Assign the static WindowProc
        WindowClass.lpfnWndProc = &MainWindow::StaticWindowProc;
        // Reserve space to store the instance pointer
        WindowClass.cbWndExtra  = sizeof(MainWindow*);
        // ...
    


  • 传递指向 CreateWindowExW

        m_hWnd = CreateWindowEx( /* ... */,
                                 static_cast<LPVOID>(this) );
        // SetWindowLongPtrW is called from the message handler
    }
    


  • 提取实例指针,并在第一条消息时将其存储在窗口附加数据区域中( WM_NCCREATE )发送到窗口:

  • Extract instance pointer and store it in the window extra data area when the first message (WM_NCCREATE) is sent to the window:

    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);
    }
    


  • 1:在创建窗口之后,在创建之前设置 lpfnWndProc 时,实例指针存储到窗口附加数据区中。这意味着 StaticWindowProc 将在实例指针不可用时被调用。因此,需要在 StaticWindowProc 内的 -statement,以便创建期间的消息(如

    Note 1: The instance pointer is stored into the window extra data area after the window has been created while the lpfnWndProc is set prior to creation. This means that StaticWindowProc will be called while the instance pointer is not yet available. As a consequence the if-statement inside StaticWindowProc is required so that messages during creation (like WM_CREATE) do get properly handled.

    注意1a:注1中所述的限制不适用于替代实现。实例指针将从第一个消息向前可用,并且类成员 WindowProc 将因此被调用用于所有消息。

    Note 1a: The restrictions stated under Note 1 do not apply to the alternative implementation. The instance pointer will be available going forward from the first message and the class member WindowProc will consequently be called for all messages.

    注意2:如果要销毁底层 HWND 时销毁C ++类实例, WM_NCDESTROY 是这样做的地方;它是发送到任何窗口的最后一条消息。

    Note 2: If you want to destroy the C++ class instance when the underlying HWND is destroyed, WM_NCDESTROY is the place to do so; it is the final message sent to any window.

    这篇关于我如何`std :: bind`一个非静态类成员到Win32回调函数`WNDPROC`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆