2

Is this even possible? For example, let's say I have the following:

class Window {
private:
    WNDCLASSEX wc;
public:
    inline WNDCLASSEX getWindowClass() {
        return wc;
    }
    Window();
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, LPARAM lParam, WPARAM wParam);
}

void RegisterWindow(Window win) {
    WNDCLASSEX* wc = win.getWindowClass();
    RegisterClassEx(wc);

}

Now, somewhere there is going to be a section (probably in the constructor of the Window class, where it's necessary to assign the WNDCLASSEX a WndProc, which is noted in the Window class. The only issue is that, because it's a part of a class, there an error will be raised. Thus, how is this achieved? Is it made static? Even so, if the class wraps it it is still part of the class in some way. If I create it outside of the class, that simply obliterates the point.

zeboidlund
  • 9,731
  • 31
  • 118
  • 180
  • See MFC, it's WndProc's wrapped into C++ classes – Abyx Jan 08 '12 at 19:55
  • Don't ever see MFC. For anything.g – Puppy Jan 08 '12 at 19:56
  • Don't have anything above Express edition, therefore I cannot use it. – zeboidlund Jan 08 '12 at 20:07
  • [WTL](http://wtl.sourceforge.net/) also shows how, and is much more modern style than MFC. – Ben Voigt Jan 08 '12 at 22:27
  • Duplicate: http://stackoverflow.com/questions/117792/best-method-for-storing-this-pointer-for-use-in-wndproc?rq=1 which has a great discussion: http://web.archive.org/web/20051125022758/www.rpi.edu/~pudeyo/articles/wndproc/ – Ben Nov 06 '12 at 04:47
  • Also, an easier article, but not as in-depth: http://members.gamedev.net/sicrane/articles/WindowClass.html (particularly helpful paragraph starts "the reason for this problem is...") – Ben Nov 06 '12 at 06:24

2 Answers2

4

You pass the this pointer as GWLP_USERDATA to SetWindowLongPtr, which effectively allows you to simply forward the free function to the member function.

Ben
  • 54,723
  • 49
  • 178
  • 224
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    Hmm, I'm not quite following: what do you mean by GWLP_USERDATA and SetWindowLongPtr? – zeboidlund Jan 08 '12 at 20:09
  • Well, I can't quite use that because my `HWND` hasn't been initialized yet. – zeboidlund Jan 08 '12 at 20:19
  • 1
    @Holland I'm not sure what you mean by that. After you call `CreateWindow`, call `SetWindowLongPtr`. If you want to connect everything up sooner, use the `lpParam` parameter. – David Heffernan Jan 08 '12 at 20:54
  • Understood, I got it going now. Thanks. – zeboidlund Jan 08 '12 at 21:25
  • 1
    [`SetProp`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms632594.aspx) is preferable to `SetWindowLongPtr(GWLP_USERDATA)`, since you aren't fighting for a scarce resource (the window extra bytes). – Ben Voigt Jan 08 '12 at 22:26
0

This page of official Microsoft documentation shown such class:

template <class DERIVED_TYPE> 
class BaseWindow
{
public:
    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        DERIVED_TYPE *pThis = NULL;

        if (uMsg == WM_NCCREATE)
        {
            CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
            pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);

            pThis->m_hwnd = hwnd;
        }
        else
        {
            pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
        }
        if (pThis)
        {
            return pThis->HandleMessage(uMsg, wParam, lParam);
        }
        else
        {
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
    }

    BaseWindow() : m_hwnd(NULL) { }

    BOOL Create(
        PCWSTR lpWindowName,
        DWORD dwStyle,
        DWORD dwExStyle = 0,
        int x = CW_USEDEFAULT,
        int y = CW_USEDEFAULT,
        int nWidth = CW_USEDEFAULT,
        int nHeight = CW_USEDEFAULT,
        HWND hWndParent = 0,
        HMENU hMenu = 0
        )
    {
        WNDCLASS wc = {0};

        wc.lpfnWndProc   = DERIVED_TYPE::WindowProc;
        wc.hInstance     = GetModuleHandle(NULL);
        wc.lpszClassName = ClassName();

        RegisterClass(&wc);

        m_hwnd = CreateWindowEx(
            dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
            nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
            );

        return (m_hwnd ? TRUE : FALSE);
    }

    HWND Window() const { return m_hwnd; }

protected:

    virtual PCWSTR  ClassName() const = 0;
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;

    HWND m_hwnd;
};

class MainWindow : public BaseWindow<MainWindow>
{
public:
    PCWSTR  ClassName() const { return L"Sample Window Class"; }
    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};

LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(m_hwnd, &ps);
            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
            EndPaint(m_hwnd, &ps);
        }
        return 0;

    default:
        return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
    }
    return TRUE;
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    MainWindow win;

    if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))
    {
        return 0;
    }

    ShowWindow(win.Window(), nCmdShow);

    // Run the message loop.

    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

DJm00n
  • 1,083
  • 5
  • 18