Search code examples
c++winapivisual-studio-2019subclassing

C++ Winapi - wrapping up a callback of subclass in a class


I wanted to create a class that I will be used to create a static control. The problem is when subclassing the control, the callback should be static which means I won't be able to access non-static variables and function inside the callback.

I've been pulling my hair to make my code works but still no success.

MyClass.cpp

#include "MyClass.h"

MyClass::MyClass(){
    non_static_variable = 0; //default is zero.
}

LRESULT CALLBACK MyClass::SubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {

    MyClass* pThis = reinterpret_cast<MyClass*>(dwRefData);
    
    char buffer[5];
    sprintf_s(buffer, "The value of non_static_variable is: %d \n", pThis->non_static_variable);
    OutputDebugStringA(buffer);    
        
    switch (uMsg) {
        case WM_PAINT: {
            // do nothing for now
            return 0;
        }

        case WM_NCDESTROY: {
            RemoveWindowSubclass(hwnd, SubClassProc, uIdSubclass);
            break;
        }
    }        
    return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}

void MyClass::CreateStaticControl(HWND WindowHandle) {
    
    StaticControl = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, x, y, width, height, WindowHandle, NULL, NULL, this); 
    SetWindowSubclass(StaticControl, &SubClassProc, ID, reinterpret_cast<DWORD_PTR>(this));
}

void MyClass::SetValue(int value){
    non_static_variable = value; //test if I can access this in callback
}

MyClass.h

#include "header.h"

public:
    MyClass();

    void CreateStaticControl(HWND window);
    void SetValue(int value);
    static LRESULT CALLBACK SubClassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

private:
    int non_static_variable;
    HWND StaticControl;

};

Then call the class inside my WM_CREATE from my main window procedure, I used it like this.

...
case WM_CREATE:{
   MyClass myClass;
   myClass.CreateStaticControl(hwnd);
   myClass.SetValue(888);
   break;
}

You might think that this is a duplicate of others, I found a lot of them but since this is about subclassing and I don't have access to WM_NCCREATE I can't properly set the pointer of my class. Can anyone help me?

EDIT: I put the exact code I have.


Solution

  • You can use the dwRefData parameter of SetWindowSubclass() to pass the this pointer to the dwRefData parameter of the callback. You can then type-cast that parameter to access non-static members. No need to use GWLP_USERDATA at all (especially since you are not using the callback's lParam correctly when setting GWLP_USERDATA).

    Try this instead:

    MyClass.h

    class MyClass {
    
    public:
        MyClass();
    
        void CreateStaticControl(HWND window);
        static LRESULT CALLBACK SubClassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
    
    private:
        int non_static_variable;
        HWND StaticControl;
    
    };
    

    MyClass.cpp:

    void MyClass::CreateStaticControl(HWND WindowHandle) {
        StaticControl = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, x, y, width, height, WindowHandle, NULL, NULL, this); 
        SetWindowSubclass(StaticControl, &SubClassProc, ID, reinterpret_cast<DWORD_PTR>(this));
    }
    
    LRESULT CALLBACK MyClass::SubClassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
    
        MyClass* pThis = reinterpret_cast<MyClass*>(dwRefData);
            
        switch (uMsg) {
            case WM_PAINT: {
                    
                // use pThis->non_static_variable as needed...
                    
                return 0;
            }
    
            case WM_NCDESTROY: {
                RemoveWindowSubclass(hwnd, SubClassProc, uIdSubclass);
                break;
            }
        }        
    
        return DefSubclassProc(hwnd, uMsg, wParam, lParam);
    }