Win32 C++ Create a Window and Procedure Within a Class

c++, class, winapi

Solution

The main message loop must not be in your class, and especially not in a "CreateTestWindow" function, as you will not return from that function until your thread receive the `WM_QUIT` message that makes `GetMessage` returns 0.

Here is simple implementation of your `viewvars` class. Key points:

- The Window Proc is a static member.

- The link between the Window Proc and the object is made through the use of GWLP_USERDATA. See SetWindowLongPtr.

- The class DTOR destroys the window if it still exists. The WM_DESTROY message set the HWND member to 0.

- Adding OnMsgXXX methods to the class is simple: declare/define then and just call them from the WindowProc using the 'this' pointer stored in GWLP_USERDATA.

EDIT:

- As per Mr Chen suggestion, earlier binding of the HWND to the Object (in WM_NCCREATE) to allow message handler as methods during the Window Creation.

I changed the creation styles, to show the window and to be able to move it.

// VIEWVARS.H
class viewvars {

public:
    static viewvars* CreateTestWindow( HINSTANCE hInstance );
    viewvars() : m_hWnd( 0 ) {}
    ~viewvars();

private:
    static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
    static const char * m_pszClassName;
    HWND m_hWnd;

};

// VIEWVARS.CPP
#include "viewvars.h"

const char * viewvars::m_pszClassName = "viewvars";

viewvars * viewvars::CreateTestWindow( HINSTANCE hInst ) {

    WNDCLASS wincl;
    if (!GetClassInfo(hInst, m_pszClassName, &wincl)) {
        wincl.style = 0;
        wincl.hInstance = hInst;
        wincl.lpszClassName = m_pszClassName;
        wincl.lpfnWndProc = WindowProc;
        wincl.cbClsExtra = 0;
        wincl.cbWndExtra = 0;
        wincl.hIcon = NULL;
        wincl.hCursor = NULL;
        wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
        wincl.lpszMenuName = NULL;
        if (RegisterClass(&wincl) == 0) {
            MessageBox(NULL,"The window class failed to register.","Error",0);
            return 0;
        }
    }

    viewvars * pviewvars = new viewvars;
    HWND hWnd = CreateWindow( m_pszClassName, "Test", WS_VISIBLE | WS_OVERLAPPED, 50, 50, 200, 200, NULL, NULL, hInst, pviewvars );
    if ( hWnd == NULL ) {
        delete pviewvars;
        MessageBox(NULL,"Problem creating the window.","Error",0); 
        return 0; 
    }

    return pviewvars;

}

 LRESULT CALLBACK viewvars::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {

    switch ( uMsg ) {

        case WM_NCCREATE: {
            CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
            viewvars * pviewvars = (viewvars*)pcs->lpCreateParams;
            pviewvars->m_hWnd = hwnd;
            SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG)pcs->lpCreateParams );
            return TRUE;
        }

        case WM_DESTROY: {
            viewvars * pviewvars = (viewvars *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
            if ( pviewvars ) pviewvars->m_hWnd = 0;
            break;
        }

        default:
            return DefWindowProc( hwnd, uMsg, wParam, lParam );

    }

    return 0;

}

viewvars::~viewvars() {
    if ( m_hWnd ) DestroyWindow( m_hWnd );
}

Finally, a "main" sample, but beware that there is here no way to end the process. That should be taken care by regular code (another windows).

// MAIN.CPP
#include <Windows.h>
#include "viewvars.h"

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    viewvars * pviewvars = viewvars::CreateTestWindow( hInstance );
    if ( pviewvars == 0 ) return 0;

    BOOL bRet;
    MSG msg;
    while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
    { 
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }
    }

    delete pviewvars;

    return 0;

}

Problem

Pre-Text/ Question I am trying to make a fairly simple tool to help debug variable values. For it to be completely self contained within the class is what I am aiming for. The end product I can use a function in the class like ShowThisValue(whatever). The problem I am having is that I can't figure out, if possible, to have the procedure within the class. Here is the short version, with the problem. -Code updated again 11/29/13- -I have put this in its own project now. [main.cpp] ``` viewvars TEST; // global TEST.CreateTestWindow(hThisInstance); // in WinMain() right before ShowWindow(hwnd, nFunsterStil); ``` [viewvars.h] The entire updated ``` class viewvars { private: HWND hWindow; // the window, a pointer to LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); public: viewvars(); // blank constructor int CreateTestWindow(HINSTANCE hInst); }; // blank constructor viewvars::viewvars() {} // create the window int viewvars::CreateTestWindow(HINSTANCE hInst) { // variables char thisClassName[] = "viewVars"; MSG msg; WNDCLASS wincl; // check for class info and modify the info if (!GetClassInfo(hInst, thisClassName, &wincl)) { wincl.style = 0; wincl.hInstance = hInst; wincl.lpszClassName = thisClassName; wincl.lpfnWndProc = &ThisWindowProc; wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hIcon = NULL; wincl.hCursor = NULL; wincl.hbrBackground = (HBRUSH)COLOR_BTNSHADOW; wincl.lpszMenuName = NULL; if (RegisterClass(&wincl) == 0) { MessageBox(NULL,"The window class failed to register.","Error",0); return -1; } } // create window hWindow = CreateWindow(thisClassName, "Test", WS_POPUP | WS_CLIPCHILDREN, 10, 10, 200, 200, NULL, NULL, hInst, this); if (hWindow == NULL) { MessageBox(NULL,"Problem creating the window.","Error",0); return -1; } // show window ShowWindow(hWindow, TRUE); // message loop while (GetMessage(&msg, hWindow, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // then quit window? DestroyWindow(hWindow); hWindow = NULL; return msg.wParam; } // window proc LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { MessageBox(NULL,"Has it gone this far?","Bench",0); // variable viewvars *view; // ???? if (message == WM_NCCREATE) { CREATESTRUCT *cs = (CREATESTRUCT*)lParam; view = (viewvars*) cs->lpCreateParams; SetLastError(0); if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0) { if (GetLastError() != 0) { MessageBox(NULL,"There has been an error near here.","Error",0); return FALSE; } } } else { view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA); } if (view) return view->WindowProc(message, wParam, lParam); MessageBox(NULL,"If shown, the above statement did not return, and the statement below did.","Error",0); return DefWindowProc(hwnd, message, wParam, lParam); } LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { // you can access non-static members in here... MessageBox(NULL,"Made it to window proc.","Error",0); switch (message) { case WM_PAINT: return 0; break; case WM_DESTROY: PostQuitMessage(0); return 0; break; default: MessageBox(NULL,"DefWindowProc Returned.","Error",0); return DefWindowProc(hWindow, message, wParam, lParam); break; } } ``` The message boxes appear in this order: - Has it made it this far? - Made it to window proc - DefWindowProc returned - Has it made it this far? // repeated? - Made it to window proc - DefWindowProc returned - Problem Creating the Window Thanks for the help so far. Do you know where the problem might be?

Original source