Почему мой WndProc не может быть в классе?

Кажется, это должно быть довольно просто. У меня есть класс:

class Simple
{
public:
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
         ...
    }
};

и my WinMain:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR commandLine, int cmdShow)
{
    Simple *simple = new Simple();
    ...

    wndClass.lpfnWndProc = simple->WndProc;
    ...
 }

Когда я пытаюсь, я получаю:

error C2440: '=' :cannot convert from 'LRESULT (__stdcall Simple::* )(HWND,UINT,WPARAM,LPARAM)' to 'WNDPROC'

Есть ли причина, по которой я не могу иметь WndProc в классе? Похоже, это было бы очень полезно.

Ответ 1

С++ рассматривает функции-члены и свободные функции, поскольку функции-члены-члены должны иметь доступ к указателю this, и обычно это передается как скрытый первый параметр. Следовательно, функция-член n-аргумента будет больше всего похожа на функцию (n + 1) -аргумента, что означает, что код, пытающийся вызвать ваш WndProc, будет содержать неправильное количество аргументов.

Вы можете, однако, объявить WndProc как функцию члена static, которая устраняет указатель this. Этот код должен работать:

class Simple
{
public:
    static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
         ...
    }
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR commandLine, int cmdShow)
{
    Simple *simple = new Simple();
    ...

    wndClass.lpfnWndProc = simple->WndProc;
    ...
 }

Конечно, это означает, что вы не можете напрямую обращаться к полям класса. Вы можете обойти это, введя указатель на класс в дополнительные байты, зарезервированные для каждого экземпляра окна, возможно, используя SetWindowLongPtr. Как только вы это сделаете, вы можете восстановить указатель объекта получателя, написав что-то вроде этого:

class Simple
{
public:
    static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
         Simple* me = static_cast<Simple*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
         if (me) return me->realWndProc(hwnd, msg, wParam, lParam);
         return DefWindowProc(hwnd, msg, wParam, lParam);
    }
private:
    LRESULT CALLBACK realWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
         // Yay!  I'm a member function!
    }
};

Надеюсь, это поможет!

Ответ 3

// Header  
class Foo   
{  
public:
  Foo();   

  ~Foo();  

  static LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM);

private:
  LRESULT CALLBACK MyWinProc(HWND, UINT, WPARAM, LPARAM);

  static Foo *m_pInstance;
}

// Implementation

#include "Foo.h"

Foo * Foo::m_pInstance = NULL;

Foo::Foo()
{  
  m_pInstance = this;   
}  

Foo::~Foo()
{
}

LRESULT CALLBACK Foo::WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
{  
  return m_pInstance->MyWinProc(hWnd, message, wParam, lParam);  
}  

LRESULT CALLBACK Foo::MyWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
{  
  return DefWindowProc(hWnd, message, wParam, lParam);  
}