Member function as callback

Started by
4 comments, last by Hodgman 6 years, 8 months ago

Hi,

  I started reading Introduction to 3D Game Programming with Direct3D 11.0 and have a little question about callback function. In author's example code d3dApp.cpp, he managed to assign a member function to WNDCLASS::lpfnWndProc


namespace
{
    // This is just used to forward Windows messages from a global window
    // procedure to our member function window procedure because we cannot
    // assign a member function to WNDCLASS::lpfnWndProc.
    D3DApp* gd3dApp = 0;
}

LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // Forward hwnd on because we can get messages (e.g., WM_CREATE)
    // before CreateWindow returns, and thus before mhMainWnd is valid.
    return gd3dApp->MsgProc(hwnd, msg, wParam, lParam);
}

in constructor D3DApp::D3DApp()


gd3dApp = this;

and in bool D3DApp::InitMainWindow()


wc.lpfnWndProc   = MainWndProc; 

Notice that D3DApp::MsgProc is a virtual function. 

As far as I'm concerned, I would find it convenient to declare MsgProc member function as static. However, a static member can't be virtual. Is there any solution so that I can overcome the contradiction except author's method?

 
Advertisement

As far as I know there is no way to assign a member function to lpfnWndProc. The author of the book assigned a proxy function to lpfnWndProc, and that function calls the member function MsgProc.

Static member functions, as you said, can't be virtual but you can design it so that it calls virtual member functions for each message type (OnCreate, OnPaint, OnDestroy, etc).

The classic trick for doing this (it's from back in the old MFC days in the 90's) is to associate a pointer with your window using SetWindowLongPtr with GWLP_USERDATA. Then you set up a free function that's a proxy message handler, and that proxy handler grabs the pointer using GetWindowLongPtr. Here's an example from my very simple window wrapper class:


LRESULT WINAPI Window::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_NCCREATE:
        {
            LPCREATESTRUCT pCreateStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
            ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
            return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
        }
    }
    Window* pObj = reinterpret_cast<Window*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    if(pObj)
        return pObj->MessageHandler(hWnd, uMsg, wParam, lParam);
    else
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}

15 hours ago, MJP said:

The classic trick for doing this (it's from back in the old MFC days in the 90's) is to associate a pointer with your window using SetWindowLongPtr with GWLP_USERDATA. Then you set up a free function that's a proxy message handler, and that proxy handler grabs the pointer using GetWindowLongPtr. Here's an example from my very simple window wrapper class:

 



LRESULT WINAPI Window::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_NCCREATE:
        {
            LPCREATESTRUCT pCreateStruct = reinterpret_cast<LPCREATESTRUCT>(lParam);
            ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCreateStruct->lpCreateParams));
            return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
        }
    }
    Window* pObj = reinterpret_cast<Window*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    if(pObj)
        return pObj->MessageHandler(hWnd, uMsg, wParam, lParam);
    else
        return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}

 

OK, I'd say that I prefer the author's method... Thanks for your help!

1 hour ago, AireSpringfield said:

OK, I'd say that I prefer the author's method... Thanks for your help!

Until you ever need two windows in your program ;)

This topic is closed to new replies.

Advertisement