gpt4 book ai didi

c++ - 如何将非静态类成员 `std::bind` 转换为 Win32 回调函数 `WNDPROC`?

转载 作者:可可西里 更新时间:2023-11-01 18:28:20 24 4
gpt4 key购买 nike

我正在尝试将非静态类成员绑定(bind)到标准 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".

最佳答案

虽然 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 的签名与您提供的代码中的签名相同。

这是实现所需行为的一种方法。 Remy Lebeau 对此提出了一个变体,它的好处是让所有消息都通过类成员 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);
    }

注意 1:当 lpfnWndProc 在创建前设置时,实例指针在窗口创建后存储到窗口额外数据区。这意味着 StaticWindowProc 将在实例指针尚不可用时被调用。因此,StaticWindowProc 中的 if 语句是必需的,以便在创建期间的消息(如 WM_CREATE)得到正确处理。

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

注2:如果你想在底层HWND被销毁时销毁C++类实例,WM_NCDESTROY是这样做的地方;它是发送到任何窗口的最终消息。

关于c++ - 如何将非静态类成员 `std::bind` 转换为 Win32 回调函数 `WNDPROC`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18161680/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com