WM_DESTROY not called inside wrapped WndProc - c++

I have adopted the typical solution you find out there in order to use the WNDPROC as an object method, but it looks like the WM_DESTROY message is not sent to the object window's own WNDPROC and the program does not exit after closing the window.
My window class looks like this (irrelevant code removed):
class MyWindow : MyApp
{
public:
MyWindow();
void Create(void);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
private:
HWND _hWnd;
};
void MyWindow::Create()
{
// Here I register my class and call CreateWindowEx
// Everything works fine so far
// Part of the code where I assign the static WNDPROC
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = MyApp::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = this->Instance;
wcex.hIcon = LoadIcon(this->Instance, MAKEINTRESOURCE(32512));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "MyWindowClass";
wcex.hIconSm = LoadIcon(this->Instance, MAKEINTRESOURCE(32512));
RegisterClassExW(&wcex);
this->_hWnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_TOOLWINDOW,
wcex.lpszClassName,
"Window Title",
WS_POPUP,
10, 10,
600, 400,
nullptr,
nullptr,
this->Instance,
nullptr
);
ShowWindow(this->_hWnd, SW_SHOW);
UpdateWindow(this->_hWnd);
}
LRESULT CALLBACK MyWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
// If I place a MessageBox here, it shows up
}
break;
case WM_DESTROY:
// It never gets to this point
// Decrease windows count
this->WindowsCount--;
PostQuitMessage(0);
break;
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
And now a class which holds the static WNDPROC, which is assigned at creation
class MyApp
{
public:
static HINSTANCE Instance;
static int WindowsCount;
MyApp();
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
};
and implementation
LRESULT CALLBACK MyApp::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Window object
MyWindow* myWindow = NULL;
if (msg == WM_CREATE) {
myWindow = reinterpret_cast<MyWindow *>(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)myWindow);
}
else {
myWindow = reinterpret_cast<MyWindow *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
// If window not identified, identify now
if (myWindow) {
return myWindow->WndProc(hWnd, msg, wParam, lParam);
}
// Call window object's processor
return DefWindowProc(hWnd, msg, wParam, lParam);
}
The WM_CLOSE message is not caught either. I really do not understand why these messages are not passed on

You are setting the lpParam parameter of CreateWindowEx() to nullptr, so myWindow is always nullptr in MyApp::WndProc(), thus MyWindow::WndProc() is never called. You need to pass this instead of nullptr.
You are also not doing any error checking to make sure RegisterClassExW() and CreateWindowEx() succeed before calling ShowWindow()/UpdateWindow().
Also, consider using SetWindowSubclass() instead of (Get|Set)WindowLongPtr(GWLP_USERDATA). See Subclassing Controls on MSDN, and Raymond Chen's blog article on Safer Subclassing.

Related

Exception in Window Framework Win32 API

I'm new to Win32 API and want to create a window. I have a class (as follows) but I get an exception at the line return p_this->HandleMessages(msg, wParam, lParam);.
Window.h (I got this in some msdn website and modified to some extent):
#include "BaseWin.h"
template<class D_CLASS>
class Window
:public BaseWin
{
public:
int returnValue;
private:
static constexpr LPCSTR className = "Best Window in the UNIVERSE!";
public:
// declare this fn in the inherited class and use as wnd proc
virtual LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam) = 0;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
D_CLASS* p_this = nullptr;
if (msg == WM_NCCREATE) {
CREATESTRUCT* create = (CREATESTRUCT*)lParam;
p_this = (D_CLASS*)create->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)p_this);
}
else
p_this = (D_CLASS*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (p_this)
return p_this->HandleMessages(msg, wParam, lParam); // exception here
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Window(HWND parent, LPCSTR title, int id, const Point& pos, const Size& _size,
int styles = WS_OVERLAPPEDWINDOW, int retVal = 0x45, int stylesEx = 0
)
:BaseWin(parent, pos, _size, id), returnValue(retVal)
{
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC;
wc.lpfnWndProc = D_CLASS::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(nullptr);
wc.hIcon = nullptr;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = className;
wc.hIconSm = nullptr;
RegisterClassEx(&wc);
HMENU _id = nullptr;
if (!((styles & WS_CHILD) != WS_CHILD || id == -1))
_id = (HMENU)ID;
hWnd = CreateWindowEx(
stylesEx,
className, title,
styles,
Position.x, Position.y, size.x, size.y,
parent, _id, GetModuleHandle(nullptr), this
);
}
~Window()
{
UnregisterClass(className, GetModuleHandle(nullptr));
Destroy();
}
};
I'm using Visual Studio and whenever the line is reached is reached, a breakpoint is placed there are no details through which I can deduce the resaon (It's: main.exe has encountered a breakpoint).
main.cpp:
#include "Window.h"
#include "Button.h"
class MainWindow
:public Window<MainWindow>
{
private:
HMENU menuBar;
HMENU menuIt;
enum
{
BTN_KILL,
M_QUIT,
M_ADD_LOG
};
public:
MainWindow()
:Window<MainWindow>(nullptr, "Useless Manger", -1, Point(CW_USEDEFAULT, CW_USEDEFAULT), Size(640, 480), normWinStyle)
{
Button* btn = new Button(hWnd, BTN_KILL, "Kill Me", Point(), Size(300, 100));
menuBar = CreateMenu();
menuIt = CreateMenu();
AppendMenu(menuIt, MF_ENABLED, M_ADD_LOG, "Add Log");
AppendMenu(menuBar, MF_ENABLED | MF_STRING, M_QUIT, "Quit");
AppendMenu(menuBar, MF_POPUP, (UINT_PTR)menuIt, "Add");
SetMenu(hWnd, menuBar);
}
LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hWnd, &ps);
break;
}
case WM_COMMAND:
if (LOWORD(wParam) == BTN_KILL)
{
PostQuitMessage(this->returnValue);
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
};
int CALLBACK WinMain(
HINSTANCE hInst,
HINSTANCE h_p_Inst,
LPSTR nCmdLine,
int nCmdShow)
{
MainWindow* window = new MainWindow();
window->Show();
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
BaswWin.h is a class holding the current HWND, position, size, ID, etc with some functions like Show().
How do I fix this?
MainWindow::HandleMessages is called before superclass Window<T> is fully constructed. Before Window<T> is fully constructed, entry for Window<T>::HandleMessages refers to special function witch reports that pure function was called. When Window<T> is fully constructed, this entry is replaced by MainWindow::HandleMessages.
In C++ calling virtual functions when superclasses are still constructed needs special atention.
You can replace Window<T>::HandleMessage with unpure implementation.
virtual LRESULT HandleMessages(UINT msg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, msg, wparam, lparam);
}
Initialize hWnd early, because WndProc can be called before CreateWindowEx returns.
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
D_CLASS* p_this = nullptr;
if (msg == WM_NCCREATE) {
CREATESTRUCT* create = (CREATESTRUCT*)lParam;
p_this = (D_CLASS*)create->lpCreateParams;
// initialize early
p_this->hWnd = hwnd;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)p_this);
}
else
p_this = (D_CLASS*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (p_this)
// if you don't initialize early, skip when hWnd == nullptr
// if (p_this && p_this->hWnd)
return p_this->HandleMessages(msg, wParam, lParam);
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Probably you would also miss WM_DESTROY message, when MainWindow class is already destroyed, but WM_DESTROY would be generated in Window<T>::~Window<T>.
Calling PostQuitMessage(this->returnValue); in MainWindow::~MainWindow could solve this problem.

Bind Dialog Procedure as method to custom class [duplicate]

This question already has answers here:
Use class member as WNDPROC/DLGPROC with or without global
(3 answers)
Closed 7 years ago.
I am trying to work my way around the WINAPI to implement the procedure of a modeless Dialog window as member of a custom class. I am not sure it is possible, but I hoped someone knew a way. The goal is to have a procedure with access to member variables of the custom Class
I know it is possible using normal windows.
Example:
//MyClass.h
class MyClass
{
public:
bool init( ... )
static LRESULT CALLBACK redirect(HWND hWnd, UINT msg,
LPARAM lParam, WPARAM wParam);
LRESULT myWndProc(HWND hWnd, UINT msg,
LPARAM lParam, WPARAM wParam);
private:
HWND m_MainHwnd;
}
By defining a static member function that redirects the pointer to the nonstatic actual procedure handling, the procedure can be a member function:
//MyClass.cpp
MyClass::init( ... )
{
//Create the window class for the main Window//
m_windowed = windowed;
WNDCLASSEX wc; //Create a new extended windows class
wc.cbSize = sizeof(WNDCLASSEX); //Size of our windows class
wc.style = CS_HREDRAW | CS_VREDRAW; //class styles
wc.lpfnWndProc = MyClass::redirect; //Default windows procedure function
wc.cbClsExtra = NULL; //Extra bytes after our wc structure
wc.cbWndExtra = NULL; //Extra bytes after our windows instance
wc.hInstance = hInstance; //Instance to current application
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); //Title bar Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); //Default mouse Icon
wc.hbrBackground = CreateSolidBrush(0xFFFFFF); //Window bg color
wc.lpszMenuName = NULL; //Name of the menu attached to our window
wc.lpszClassName = m_windowName; //Name of our windows class
wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO); //Icon in your taskbar
if (!RegisterClassEx(&wc)) //Register our windows class
{
//if registration failed, display error
MessageBox(NULL, "Error registering Main Window class",
"Error", MB_OK | MB_ICONERROR);
return false;
}
m_MainHwnd = CreateWindowEx( //Create our Extended Window
NULL, //Extended style
m_windowName, //Name of our windows class
m_windowName, //Name in the title bar of our window
WS_OVERLAPPEDWINDOW | WS_VISIBLE, //style of our window | Make it visible on showWindow cmd
30, 30, //Top left corner of window
width, //Width of our window
height, //Height of our window
NULL, //Handle to parent window
NULL, //Handle to a Menu
hInstance, //Specifies instance of current program
this //used for an MDI client window
);
}
See the line in the setup in WNDCLASSEX: wc.lpfnWndProc = MyClass::redirect;? This is valid because the the following implementation of the redirect static function:
MyClass::redirect(HWND hwnd, UINT msg, LPARAM lParam, WPARAM wParam)
{
if (msg == WM_CREATE) SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR)((CREATESTRUCT FAR *)lParam)->lpCreateParams);
MyClass * pObject = (MyClass*)((LONG_PTR)GetWindowLongPtr(hwnd, GWL_USERDATA));
if (pObject) return pObject->MyWndProc(hwnd, msg, wParam, lParam);
//return the message for windows to handle it
return DefWindowProc(hwnd,
msg,
wParam,
lParam);
};
This lets you implement the MyWndProc ( ... ) exactly like any other member method.
Now! I really want to be able to do the same thing to my Modeless Dialog box - because it's rather important that it knows the member variables of "MyClass". Also - I like that I can design it and redesign it as I like ( If I have to use normal windows - It'd be tedious to design it all in code. )
Is it possible?
Thank you in advance!
Yes it's possible to use this method with a dialog box, in more or less the same way.
Instead of using WM_CREATE to store the this pointer you use WM_INITDIALOG. Note that the user data is passed to WM_INITDIALOG in lParam itself - there are no structures to dereference like there is with WM_CREATE.
You can store the pointer in DWLP_USER which is a pointer-size window data slot available to the owner of the dialog box.
The final difference is simply return FALSE if the data pointer hasn't been assigned yet - you don't call DefWindowProc from a dialog procedure.
class MyClass
{
public:
static INT_PTR CALLBACK dlgRedirect(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT myDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
}
//...
HWND hwndDlg = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), hwndParent,
MyClass::dlgRedirect, reinterpret_cast<LPARAM>(this));
//...
INT_PTR CALLBACK MyClass::dlgRedirect(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG) SetWindowLongPtr(hWnd, DWLP_USER, lParam);
MyClass* pThis = reinterpret_class<MyClass*>(GetWindowLongPtr(hWnd, DWLP_USER));
if (pThis) return pThis->myDlgProc(hWnd, uMsg, wParam, lParam);
return FALSE;
}
INT_PTR MyClass::myDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// real dialog procedure here
}

Why is virtual WndProc not called?

Here's what I have so far... Even though I instantiate a Window (subclass of WindowBase), I am getting the error that a pure virtual function is trying to be called. Basically, my program is trying to call WindowBase::WndProc instead of Window::WndProc.
WINDOWBASE.H
#ifndef WINDOWBASE_H_
#define WINDOWBASE_H_
#include <Windows.h>
class WindowBase {
public:
WindowBase(HINSTANCE hInstance, int nCmdShow);
~WindowBase();
void Show();
protected:
virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) = 0;
private:
static LRESULT CALLBACK WndRouter(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
HWND hWnd;
int nCmdShow;
};
#endif /* WINDOWBASE_H_ */
WINDOWBASE.CPP
#include <Windows.h>
#include "WindowBase.h"
#include <tchar.h>
WindowBase::WindowBase(HINSTANCE hInstance, int nCmdShow) {
this->nCmdShow = nCmdShow;
WNDCLASS wcex;
//wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndRouter;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = _T("TestMenu");
wcex.lpszClassName = _T("TestWindow");
//wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClass(&wcex)) {
MessageBox(NULL,
"Call to RegisterClassEx failed!",
"Win32 Guided Tour",
NULL);
}
hWnd = CreateWindow(_T("TestWindow"), _T("TestWindow"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, NULL, (void*)this);
if (!hWnd){
MessageBox(NULL,
"Call to CreateWindow failed!",
"Win32 Guided Tour",
NULL);
}
}
WindowBase::~WindowBase() {
}
void WindowBase::Show() {
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
}
LRESULT CALLBACK WindowBase::WndRouter(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
WindowBase* base = NULL;
if (uMsg == WM_NCCREATE) {
base = reinterpret_cast<WindowBase*>(((LPCREATESTRUCT)lParam)->lpCreateParams);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)base);
} else {
base = reinterpret_cast<WindowBase*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if (!base)
return DefWindowProc(hWnd, uMsg, wParam, lParam);
return base->WndProc(hWnd, uMsg, wParam, lParam); // GETS TO HERE, BUT TRIES TO
// CALL WindowBase::WndProc, INSTEAD OF Window::WndProc
}
WINDOW.H
#ifndef WINDOW_H_
#define WINDOW_H_
#include "windowbase.h"
class Window : public WindowBase {
public:
Window(HINSTANCE hInstance, int nCmdShow);
~Window();
protected:
virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
#endif /* WINDOW_H_ */
If you call CreateWindow in your WindowBase constructor, you start to receive messages from there.
If you create your Window object, it's own constructor have to call the WindowBase constructor. During that point, the Window object is not yet came to existence, and so its virtual functions aren't yet available (they will refer a not yet constructed Window ...).
Your design have also a number of other pitfalls: Think about the scope and lifetime of every component: some of them are used before constructed, other destroyed when still needed.
It's not that easy to wrap an OOP C API (like WIn32) into another OOP language (like C++) that have a notion of "Object" and "scope" the t don't match the one WIN32 thinks about. Take care of both of them, or you can easily get cod that looks working, but used in a more wider context (more window than just one) risk to behave not as expected.

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

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?
To use a non-static class method as a window procedure requires a dynamically-allocated thunk, which is an advanced technique that I will not get into it here.
The alternative is to declare the class method as static, then it will work as a window procedure. Of course, being a static method, it can no longer access non-static class members without an instance pointer. To get that pointer, you can have the class pass its this pointer to the lpParam parameter of CreateWindow/Ex(), then the window procedure can extract that pointer from the WM_NCCREATE message and store it in the window using SetWindowLong/Ptr(GWL_USERDATA). After that, subsequent messages can retrieve that pointer using GetWindowLong/Ptr(GWL_USERDATA) and thus be able to access non-static members of that object. For example:
class viewvars
{
private:
HWND hWindow;
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
int CreateTestWindow(HINSTANCE hInst);
};
int viewvars::CreateTestWindow(HINSTANCE hInst)
{
WNDCLASS wincl;
if (!GetClassInfo(hInst, thisClassName, &wincl))
{
...
wincl.hInstance = hInst;
wincl.lpszClassName = thisClassName;
wincl.lpfnWndProc = &ThisWindowProc;
if (RegisterClass(&wincl) == 0)
return -1;
}
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
...
MSG msg;
while (GetMessage(&msg, hWindow, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hWindow);
hWindow = NULL;
return msg.wParam;
}
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
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)
return FALSE;
}
}
else
{
view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}
if (view)
return view->WindowProc(message, wParam, lParam);
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// you can access non-static members in here...
switch (message)
{
case WM_PAINT:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWindow, message, wParam, lParam);
}
}
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;
}
Unfortunately using an instance method as a C-style callback function for the WndProc won't work. At least not in any straight-forward way.
The reason it doesn't work like that is that an instance method requires the this pointer to be passed in (to point to an instance) and that won't be correctly set by the code calling the WndProc. The Win32 API was originally designed with C in mind so this is one area where you have to use some work-arounds.
One way to work around this would be to create a static method to serve as the window proc and dispatch messages to your class instances. The class instances would have to be registered (read added to a static collection) so the static method would know to dispatch WndProc messages to the instances. Instances would register themselves with the static dispatcher in the constructor and remove themselves in the destructor.
Of course all the registration and unregistration and dispatching overhead is only necessary if your WndProc handler needs to invoke other instance member functions, or access member variables. Otherwise you can just make it static and you're done.
Your window procedure is called during CreateWindow. You pass hWindow to DefWindowProc, but hWindow is only set after CreateWindow returns - so you pass DefWindowProc a garbage window handle.
I don't see a nice way to do it. You could set hWindow inside the window procedure, by changing WindowProc to:
LRESULT WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
(added the hwnd parameter), changing the call to:
return view->WindowProc(hwnd, message, wParam, lParam);
creating the window like this:
hWindow = NULL;
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
return -1;
(the first assignment is to make sure hWindow is initialized; the second one is in case CreateWindow fails after calling the window procedure), and adding this at the start of WindowProc:
if(!this->hWindow)
this->hWindow = hwnd;
Step through the code in the debugger. When you get to the line
MessageBox(NULL,"DefWindowProc Returned.","Error",0);
return DefWindowProc(hWindow, message, wParam, lParam);
You will see something wrong: hWindow is garbage. You are using an uninitialized variable.

winapi C++ Handling Focus

I am working on a C++ winapi app and I am struggling to get SetFocus() to work for me. I create the Main window with WS_OVERLAPPEDWINDOW | WS_VISIBLE and then within that I create its children (a couple of buttons and the beginnings of my own version of an Edit control) with WS_CHILD | WS_VISIBLE, which are all working fine except for the issue of focus.
In my searches I have battled to find much in terms of how you should handle Focus. When the windows are all created they individually receive the WM_SETFOCUS message and I handle this message in my edit control by creaing the caret, however it seems the children never receive the WM_KILLFOCUS message and so caret is never destroyed.
This is now where my problem comes in: I would like the main parent window to initially have focus and for there to be no caret in my edit control and then when the child Edit control is clicked for it have focus and then when the main window is clicked it should then have focus again and so on.
So my initial thought was to use SetFocus() to set focus to Main Window when handling the WM_CREATE message however that didn't seem to work: the child don't received the WM_KILLFOCUS message.
My next thought was that maybe the parent has to handle passing down WM_KILLFOCUS to the appropriate children so I wrote a method to do that for me but the children still did not receive the WM_KILLFOCUS message.
So my best guess is that I am not handling the Messages correctly in my WndProc.
I have created my own Window class and distribute the messages to the appropriate classes through the following WndProc:
LRESULT CALLBACK CBaseWindow::stWinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CBaseWindow* pWnd;
if (uMsg == WM_NCCREATE)
{
SetWindowLong(hwnd, GWL_USERDATA, (long)((LPCREATESTRUCT(lParam))->lpCreateParams));
}
pWnd = GetObjectFromWindow(hwnd);
if (pWnd)
return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);
else
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Then each class has its own WndProc where I handle the messages as they come in.
So does anyone have any thoughts for me?
If I am going about this is complete wrong way or if I am not following best practice please say so, I am doing this to learn so shoot away.
[UPDATE]
OK here is some code to demonstrate the problem:
Main.cpp
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "MainWnd.h"
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MainWnd wnd(hInstance);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0 ) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
BaseWindow.cpp
#include "BaseWindow.h"
//...
LRESULT CALLBACK CBaseWindow::stWinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CBaseWindow* pWnd;
if (uMsg == WM_NCCREATE)
{
SetWindowLong(hwnd,
GWL_USERDATA,
(long)((LPCREATESTRUCT(lParam))->lpCreateParams));
}
pWnd = GetObjectFromWindow(hwnd);
if (pWnd)
return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);
else
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
BOOL CBaseWindow::Create(DWORD dwStyles, RECT* rect)
{
m_hwnd = CreateWindow(
szClassName,
szWindowTitle,
dwStyles,
rect->left,
rect->top,
rect->right - rect->left,
rect->bottom - rect->top,
NULL,
NULL,
hInstance,
(void *)this);
return (m_hwnd != NULL);
}
MainWnd.cpp
#include "MainWnd.h"
#define WIDTH 400
#define HEIGHT 400
MainWnd::MainWnd(HINSTANCE hInst): CBaseWindow(hInst), hInstance(hInst)
{
SetWindowTitle(_T("Main Window"));
WNDCLASSEX wcx;
FillWindowClass(&wcx);
if(RegisterWindow(&wcx))
{
RECT rc;
BuildRect(&rc);
if(Create(WS_OVERLAPPEDWINDOW | WS_VISIBLE, &rc))
{
customTextBox = new CustomTextBox(hInst, m_hwnd);
}
}
}
void MainWnd::FillWindowClass(WNDCLASSEX *wcx)
{
wcx->cbSize = sizeof(WNDCLASSEX);
wcx->style = CS_HREDRAW | CS_VREDRAW | CS_DROPSHADOW;
wcx->lpfnWndProc = CBaseWindow::stWinMsgHandler;
wcx->cbClsExtra = 0;
wcx->cbWndExtra = 0;
wcx->hInstance = hInstance;
wcx->hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx->hCursor = LoadCursor(NULL, IDC_ARROW);
wcx->hbrBackground = CreateSolidBrush(RGB(255,255,255));
wcx->lpszMenuName = NULL;
wcx->lpszClassName = _T("MainWindow");
wcx->hIconSm = LoadIcon(NULL, IDI_APPLICATION);
}
LRESULT CALLBACK MainWnd::WinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
delete customTextBox;
PostQuitMessage(0);
break;
case WM_LBUTTONUP:
SetFocus(hwnd);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
CustomTextBox.cpp
#include "CustomTextBox.h"
CustomTextBox::CustomTextBox(
HINSTANCE hInst,
HWND hParent): CBaseWindow(hInst),
hParent(hParent),
{
WNDCLASSEX wcx;
CreateWndClassEX(wcx);
if(RegisterWindow(&wcx))
{
RECT clientRect;
CreateClientRect(clientRect);
CreateChild(WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP, &clientRect, hParent);
}
}
void CustomTextBox::CreateWndClassEX(WNDCLASSEX& wcx)
{
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = CBaseWindow::stWinMsgHandler;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = CreateSolidBrush(RGB(255,255,255));
wcx.lpszMenuName = NULL;
wcx.lpszClassName = _T("Edit Control");
wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
}
LRESULT CALLBACK CustomTextBox::WinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch(uMsg)
{
/* Handling the caret */
case WM_SETFOCUS:
CreateCaret(hwnd, NULL, 0, nWindowY);
SetCaretPos(GetEndOfLinePoint(), nCaretPosY * nCharY);
ShowCaret(hwnd);
return 0;
case WM_MOUSEACTIVATE:
SetFocus(hwnd);
return MA_ACTIVATE;
case WM_KILLFOCUS:
DestroyCaret();
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
Discovery
In writing up this code I came across one of the causes of my problems: in my actual application I don't have a title bar and so to move the window I was sending WM_NCLBUTTONDOWN to my main window on WM_LBUTTONDOWN:
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
I then had my SetFocus() after this and this wasn't working but if I switch them around and handle the SetFocus() first then the clicking does change focus.
There is still a problem with initially setting the focus though. At the moment after starting the app the custom edit control still displays the caret even though it doesn't have focus and you need to click on it to give it focus, whereby it will receive keyboard input. After that the focus works as desired: if I click on the main window it has focus; if I click on the custom edit, it has focus etc.
OK so it turns out that my entire problem lied with that line SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL); Once I switched this:
case WM_LBUTTONDOWN:
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
SetFocus(hwnd);
break;
to:
case WM_LBUTTONDOWN:
SetFocus(hwnd);
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
break;
everything came together.