Search code examples
c++winapivisual-studio-2015com

Do CustomDialogProc have to be static. WinAPI


I've been looking at creating a custom control with WinApi for my application, and I have made a class which contains the CustomDialogProc and CreateWindowEx and RegisterClass() functions.

I can set a breakpoint inside the CustomDialogProc and it hits, so the class is registered correctly.

However, I have to declare the CustomDialogProc function as static int he header of my class

static LRESULT CALLBACK CustomDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam);

If I don't set it to static, I get the error

Error   C3867   'CustomControl::CustomDialogProc': non-standard syntax; use '&' to create a pointer to member   

IS this necessary, this requires all my controls created within this control to be static as well. What if I want multiple instances of this control? How can I get around this? The main MsgProc doesn't seem to be a static function. Neither is the UpDownDialogProc in the first link shown below

Below is my code for CustomControl.h in case anyone needs it. Put together from code found at: https://msdn.microsoft.com/en-us/library/windows/desktop/hh298353(v=vs.85).aspx https://www.codeproject.com/Articles/485767/True-Windows-control-subclassing

Thanks,

#pragma once
#include <windows.h>

#include <commctrl.h>

#pragma comment(lib, "comctl32.lib")

class CustomControl
{
public:
    CustomControl();
    ~CustomControl();

    LRESULT CALLBACK CustomDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam)
    {
        switch (message)
        {
            case WM_CREATE:
            //DO STUFF HERE
            break;
        }
    }

    bool CreateControl(HWND hwnd, HINSTANCE* m_hApp_instance)
    {
        g_hInst = m_hApp_instance;

        RegisterSubClass(*g_hInst, WC_LISTBOX, TEXT("CustomControl"), CustomDialogProc);

        HWND hwndCustom = CreateWindow(TEXT("CustomControl"), NULL, WS_CHILD | WS_VISIBLE,
        0, 0, 0, 0, hwnd, (HMENU)100, *g_hInst, NULL);

        return true;
    }

private:

    HINSTANCE* g_hInst;

    WNDPROC RegisterSubClass(HINSTANCE hInstance, LPCTSTR ParentClass, LPCTSTR ChildClassName, WNDPROC ChildProc) {
        WNDCLASSEX  twoWayStruct;
        WNDPROC     parentWndProc;

        if (GetClassInfoEx(NULL, ParentClass, &twoWayStruct)) {
            parentWndProc = twoWayStruct.lpfnWndProc; // save the original message handler 

            twoWayStruct.cbSize = sizeof(WNDCLASSEX); // does not always get filled properly
            twoWayStruct.hInstance = hInstance;
            twoWayStruct.lpszClassName = ChildClassName;
            twoWayStruct.lpfnWndProc = ChildProc;

            /* Register the window class, and if it fails return 0 */
            if (RegisterClassEx(&twoWayStruct))
                return parentWndProc; // returns the parent class WndProc pointer;
                                      // subclass MUST call it instead of DefWindowProc();
                                      // if you do not save it, this function is wasted
        }
        return 0;
    }
};

Solution

  • The most common way is to use SetWindowLongPtr to store a pointer to the object associated with the window handle.

    HWND hWnd = CreateWindow(...);
    SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) this);
    

    And then in your dialog proc, get that pointer and call into your class:

    // this static method is registered with your window class
    static LRESULT CALLBACK CustomDialogProcStatic(HWND hWnd, UINT uMsg, WPARAM wParam,LPARAM lParam)
    {
        auto pThis = (CustomControl*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
        if (pThis != NULL)
            return pThis->CustomDialogProcInstance(hWnd, uMsg, wParam, lParam);
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    
    // this instance method is called by the static method
    LRESULT CustomDialogProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        ...
    }
    

    Make sure you manage your window and class life cycle appropriately to prevent the window proc from calling a deleted object instance. In many cases, this is as simple as ensuring DestroyWindow is called if your class is destructed.