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?