Best method for storing this pointer for use in WndProc

Question!

I'm interested to know the best / common way of storing a this pointer for use in the WndProc. I know of several approaches, but each as I understand it have their own drawbacks. My questions are:

What different ways are there of producing this kind of code:

CWindow::WndProc(UINT msg, WPARAM wParam, LPARAM)
{
  this->DoSomething();
}

I can think of Thunks, HashMaps, Thread Local Storage and the Window User Data struct.

What are the pros / cons of each of these approaches?

Points awarded for code examples and recommendations.

This is purely for curiosities sake. After using MFC I've just been wondering how that works and then got to thinking about ATL etc.

Edit: What is the earliest place I can validly use the HWND in the window proc? It is documented as WM_NCCREATE - but if you actually experiment, that's not the first message to be sent to a window.

Edit: ATL uses a thunk for accessing the this pointer. MFC uses a hashtable lookup of HWNDs.



Answers

While using the SetWindowLongPtr and GetWindowLongPtr to access the GWL_USERDATA might sound like a good idea, I would strongly recommend not using this approach.

This is the exactly the approached used by the Zeus editor and in recent years it has caused nothing but pain.

I think what happens is third party windows messages are sent to Zeus that also have their GWL_USERDATA value set. One application in particular was a Microsoft tool that provied an alternative way to enter Asian characters in any windows application (i.e. some sort of software keyboard utility).

The problem is Zeus always assumes the GWL_USERDATA data was set by it and tries to use the data as a this pointer, which then results in a crash.

If I was to do it all again with, what I know now, I would go for a cached hash lookup approach where the window handle is used as the key.

By : jussij


I recommend setting a thread_local variable just before calling CreateWindow, and reading it in your WindowProc to find out the this variable (I presume you have control over WindowProc).

This way you'll have the this/HWND association on the very first message sent to you window.

With the other approaches suggested here chances are you'll miss on some messages: those sent before WM_CREATE / WM_NCCREATE / WM_GETMINMAXINFO.

class Window
{
    // ...
    static thread_local Window* _windowBeingCreated;
    static thread_local std::unordered_map<HWND, Window*> _hwndMap;
    // ...
    HWND _hwnd;
    // ...
    // all error checking omitted
    // ...
    void Create (HWND parentHWnd, UINT nID, HINSTANCE hinstance)
    {
        // ...
        _windowBeingCreated = this;
        ::CreateWindow (YourWndClassName, L"", WS_CHILD | WS_VISIBLE, x, y, w, h, parentHWnd, (HMENU) nID, hinstance, NULL);
    }

    static LRESULT CALLBACK Window::WindowProcStatic (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
    {
        Window* _this;
        if (_windowBeingCreated != nullptr)
        {
            _hwndMap[hwnd] = _windowBeingCreated;
            _windowBeingCreated->_hwnd = hwnd;
            _this = _windowBeingCreated;
            windowBeingCreated = NULL;
        }
        else
        {
            auto existing = _hwndMap.find (hwnd);
            _this = existing->second;
        }

        return _this->WindowProc (msg, wparam, lparam);
    }

    LRESULT Window::WindowProc (UINT msg, WPARAM wparam, LPARAM lparam)
    {
        switch (msg)
        {
            // ....
By : adigostin


You can use GetWindowLongPtr and SetWindowLongPtr; use GWLP_USERDATA to attach the pointer to the window. However, if you are writing a custom control I would suggest to use extra window bytes to get the job done. While registering the window class set the WNDCLASS::cbWndExtra to the size of the data like this, wc.cbWndExtra = sizeof(Ctrl*);.

You can get and set the value using GetWindowLongPtr and SetWindowLongPtr with nIndex parameter set to 0. This method can save GWLP_USERDATA for other purposes.

The disadvantage with GetProp and SetProp, there will be a string comparison to get/set a property.

By : Vishal


This video can help you solving your question :)
By: admin