Class method for WndProc - c++

This article explains brilliantly the options to call a class member WndProc. I've seen this response in stackoverflow but the main problem associating class member WndProc after CreateWindow is that some messages will be lost (including the important WM_CREATE) as explained in the mentioned article.
My question: I would like to hear the opinion from an expert on which of the methods exposed below or new one is the best one (performance, maintanability, ...) to create a class member WndProc.
Briefing the two final solutions exposed in the article (suposing that it exists a Window class with WndProc method):
Per-window data with this global pointer storage, protecting it with CRITICAL_SECTION to make it thread safe (extracted from here):
// The helper window procedure
// It is called by Windows, and thus it's a non-member function
// This message handler will only be called after successful SetWindowLong call
// We can assume that pointer returned by GetWindowLong is valid
// It will route messages to our member message handler
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
// Get a window pointer associated with this window
Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA);
// It should be valid, assert so
_ASSERT(w);
// Redirect messages to the window procedure of the associated window
return w->WndProc(hwnd, msg, wp, lp);
}
// The temporary global this pointer
// It will be used only between CreateWindow is called and the first message is processed by WndProc
// WARNING: it is not thread-safe.
Window *g_pWindow;
// Critical section protecting the global Window pointer
CRITICAL_SECTION g_WindowCS;
// The helper window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
// Stash global Window pointer into per-window data area
SetWindowLong(hwnd, GWL_USERDATA, (long) g_pWindow);
// Unlock global critical section
g_pWindow->HaveCSLock = false;
LeaveCriticalSection(&g_WindowCS);
// Reset the window message handler
SetWindowLong(hwnd, GWL_WNDPROC, (long) WndProc2);
// Dispatch first message to the member message handler
return WndProc2(hwnd, msg, wp, lp);
}
And now we can create the window:
InitializeCriticalSection(&g_WindowCS);
// Enter the critical section before you write to protected data
EnterCriticalSection(&g_WindowCS);
// Set global Window pointer to our Window instance
// Moved the assignment here, where we have exclusive access to the pointer
g_pWindow = &w;
// Set a flag indicating that the window has the critical section lock
// Note: this must be executed after the above assignment
g_pWindow->HaveCSLock = true;
// Create window
// Note: lpParam is not used
HWND hwnd = CreateWindow(TEXT("BaseWnd"), TEXT("Hello, World!"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, 0);
// Leave critical section if window creation failed and our window procedure hasn't released it
if (g_pWindow->HaveCSLock)
LeaveCriticalSection(&g_WindowCS);
// Destroy critical section
// In production code, you'd do this when application terminates, not immediately after CreateWindow call
DeleteCriticalSection(&g_WindowCS);
Using CBT hook procedure (extracted from here):
// The helper window procedure
// It is called by Windows, and thus it's a non-member function
// This message handler will only be called after successful SetWindowLong call from the hook
// We can assume that pointer returned by GetWindowLong is valid
// It will route messages to our member message handler
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
// Get a window pointer associated with this window
Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA);
// It should be valid, assert so
_ASSERT(w);
// Redirect messages to the window procedure of the associated window
return w->WndProc(hwnd, msg, wp, lp);
}
// The CBT hook procedure
// It is called during CreateWindow call before WndProc receives any messages
// Its job is to set per-window Window pointer to the one passed through lpParam to CreateWindow
LRESULT CALLBACK CBTProc(int code, WPARAM wp, LPARAM lp)
{
if (code != HCBT_CREATEWND) {
// Ignore everything but create window requests
// Note: generally, HCBT_CREATEWND is the only notification we will get,
// assuming the thread is hooked only for the duration of CreateWindow call.
// However, we may receive other notifications, in which case they will not be passed to other CBT hooks.
return 0;
}
// Grab a pointer passed to CreateWindow as lpParam
std::pair<Window *, HHOOK> *p = (std::pair<Window *, HHOOK> *) LPCBT_CREATEWND(lp)->lpcs->lpCreateParams;
// Only handle this window if it wasn't handled before, to prevent rehooking windows when CreateWindow is called recursively
// ie, when you create windows from a WM_CREATE handler
if (p->first) {
// Stash the associated Window pointer, which is the first member of the pair, into per-window data area
SetWindowLong((HWND) wp, GWL_USERDATA, (long) p->first);
// Mark this window as handled
p->first = 0;
}
// Call the next hook in chain, using the second member of the pair
return CallNextHookEx(p->second, code, wp, lp);
}
And now we can create the window:
// Install the CBT hook
// Note: hook the thread immediately before, and unhook it immediately after CreateWindow call.
// The hook procedure can only process window creation nofitications, and it shouldn't be called for other types of notifications
// Additionally, calling hook for other events is wasteful since it won't do anything useful anyway
HHOOK hook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
_ASSERT(hook);
// Create window
// Pass a pair consisting of window object pointer and hook as lpParam
std::pair<Window *, HHOOK> p(&w, hook);
HWND hwnd = CreateWindow(TEXT("BaseWnd"), TEXT("Hello, World!"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, &p);
// Unhook first
UnhookWindowsHookEx(hook);

I personally would not use either of these methods. The global variable approach works, but feels dirty. Especially with the lock. And the CBT hook is, well over the top. Although it points in the right direction.
The standard way to pass state information to your window procedure during creation is through lpParam parameter of CreateWindow or CreateWindowEx. So the technique is as follows:
Pass your instance pointer in the lpParam parameter of CreateWindow or CreateWindowEx.
Read this value in your WM_NCCREATE handler. That message supplies the information as part of the CREATESTRUCT struct.
Still in WM_NCCREATE call SetWindowLongPtr to set the user data of the window to the instance pointer.
All future calls to the window procedure can now obtain the instance pointer by calling GetWindowLongPtr.
Raymond Chen illustrates the details here: How can I make a WNDPROC or DLGPROC a member of my C++ class?

Related

Lifespan of smart pointer objects declared and initialized inside WndProc message (WM_CREATE)

If I declare and initialize object within main window callback function like so:
LRESULT CALLBACK WndProc
(
HWND hWnd // handle to window of this process
, UINT msg // message constant
, WPARAM wParam // holder of message parameters
, LPARAM lParam // holder of message parameters
)
{
switch (msg)
{
case WM_CREATE:
{
std::unique_ptr<Foo> foo = std::unique_ptr<Foo>(new Foo);
}
break;
}
}
Will the object foo stay initialized after the break of WM_CREATE message? And if not, where is the best place to declare it so that it's scope is not limited by the case scope?
The foo object is creating controls in the main window by the way so I think the requirement is to let it live till the end of runtime.
I suspect it will not survive. I am thinking about declaring those as global variable (but I don't like that option for obvious reasons) or as static objects within the callback function (but outside the switch). But there might be better options I don't see, so I am seeking your advice.
Thank you for help!
Here's a workup of RbMm's suggestion, which I use myself in my own code. A smart pointer has little to offer here, so there's really not much point in using one. Please note that I have used C-style casts for brevity, but you can use reinterpret_cast if you prefer.
class MyWindowData { ... };
MyWindowData *window_data = new MyWindowData;
HWND hWnd = CreateWindow (... ... ..., (LPARAM) window_data);
LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE:
CREATESTRUCT *cs = (CREATESTRUCT *) lParam;
SetWindowLongPtr (hWnd, GWLP_USERDATA, cs->lpCreateParams);
break;
case NC_DESTROY:
MyWindowData *window_data = (MyWindowData *) GetWindowLongPtr (hWnd, GWLP_USERDATA);
SetWindowLongPtr (hWnd, GWLP_USERDATA, 0);
delete window_data;
break;
}
// return something here;
}
You can then do:
MyWindowData *window_data = (MyWindowData *) GetWindowLongPtr (hWnd, GWLP_USERDATA);
to retrieve MyWindowData any time you need it, but check for zero being returned in case WM_NCCREATE is not the first message received by your WNDPROC as per the comments.

Subclassing an edit control from a user defined class/pointer-to-member-function

I think I've fallen in the same trap as many before me where I try to impose a nice OO methodology on win32 API programming. No MFC, no AFX, I'm not even using VC++, I'm using C::B with gcc.
I think what I'm trying to do is impossible, but since MFC exists (although I'm not using it) there must be some way.
I've created a class to contain several window controls. It implements handlers for WM_CREATE and WM_COMMAND, and keeps track of all the associated data around my small group of controls (ID codes and HWNDs).
It works great for buttons, static controls, even light GDI methods, but it all breaks down when I try to subclass an edit control.
Really, I just want to capture the "enter" key, but as anybody who's been down that road before will attest, when an edit control has focus, the parent window doesn't receive WM_KEYDOWN or WM_COMMAND, we are left to implement our own proc. Super lame.
OK, so subclassing an edit control is fine, if the editProc is global or static. I know that is because SetWindowLongPtr needs a function address, and that concept is nebulous for a member function.
So the object of my class is declared as "static" inside the parent WndProc. But the function is not "static" because then I wouldn't have access to non-static data members (completely defeating the purpose of this exercise). I'm hoping that because the objest is itself static, I should be able to properly define the address of one of its member functions.
Readers that have tried this before will either have given up and used MFC or something else, or perhaps have found a clever work-around.
I'll let this sample code do the rest of the talking: (simplified - will not compile as such)
/**** myprogram.c ****/
#include "MyControlGroup.h"
int winMain(){ // etc... }
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// object is static becuse it only needs to be initialized once
static MyControlGroup myControl;
if (msg == WM_CREATE)
myControl.onWMCreate(hWnd);
else if (msg == WM_COMMAND)
myControl.onWMCommand( wParam, lParam );
else if (msg == WM_DESTROY)
PostQuitMessage(0);
return DefWindowProcW(l_hWnd, l_msg, l_wParam, l_lParam);
}
The header file for my class:
/**** MyControlGroup.h ****/
class MyControlGroup
{
private:
HWND m_hWndParent;
HWND m_hWndEditBox;
int m_editBoxID;
public:
MyControlGroup();
void onWMCreate(HWND);
void onWMCommand(WPARAM, LPARAM);
// want to find a way to pass the address of this function to SetWindowLongPtr
LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
};
...and the implementation:
/**** MyControlGroup.cpp ****/
static int staticID = 1;
MyControlGroup::MyControlGroup()
{
m_editBoxID = staticID++;
}
void MyControlGroup::onWMCreate(HWND hWnd)
{
// My control group has buttons, static controls, and other stuff which are created here with CreateWindowW. It also has an edit control:
m_hWndEditBox = CreateWindowW(L"EDIT", L"initial text", WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 150, 20, hWnd, (HMENU)m_editBoxID, NULL, NULL);
/*
To subclass the edit control, I need a pointer to my customized proc. That means I
need a pointer-to-member-function, but SetWindowLongPtr needs a pointer to global or
static function (__stdcall or CALLBACK, but not __thiscall).
*/
// I'd like to do something like this, adapted from a great write-up at
// http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
LERSULT (MyControlGroup::*myEditProcPtr)(HWND, UINT, WPARAM, LPARAM);
myEditProcPtr = &MyControlGroup::myEditProc;
// Up to now it compiles ok, but then when I try to pass it to SetWindowLongPtr, I get
// an "invalid cast" error. Any ideas?
SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProcPtr);
}
void MyControlGroup::onWMCommand(WPARAM wParam, LPARAM lParam){ /* process parent window messages. Editboxes don't generate WM_COMMAND or WM_KEYDOWN in the parent :''( */}
LRESULT MyControlGroup::myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// process messages like IDOK, WM_KEYDOWN and so on in the edit control
}
Even once I get this done, I'll still need to figure out a way to pass the address of the parent WndProc to myEditProc for the return value, but until I get past this there is no point in worrying about that.
Thanks in advance for reading!
myEditProc needs to be a static function.
Once you've done that you can pass the address of the function directly without going through the intermediate variable:
static LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
...
SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProc);
To access your class data from the static function, you can save it in the userdata field of the edit control, e.g.:
// before sub-classing the control
SetWindowLongPtr(m_hWndEditBox, GWLP_USERDATA, (LPARAM)this);
// in the sub-class procedure
MyControlGroup* pThis = (MyControlGroup*)GetWindowLongPtr(m_hWndEditBox, GWLP_USERDATA);
But as #K-ballo suggested, SetWindowSubclass is definitely the way to do this unless you want compatibility with pre-XP. It handles the sub-classing procedure for you automatically, lets you associate a userdata pointer (e.g. this) that is automatically passed to the sub-class procedure, and safely handles removing the sub-class at the end.

Pass extra wParam/lParam parameters?

A standard window procedure function takes this prototype:
LRESULT CALLBACK WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
When a message such as WM_MOUSEMOVE or WM_CHAR, the WndProc function will receive the window the message originated from, and any extra parameters, which will be with msg and wParam/lParam.
What I have now is a class. Say
class Random
{
public:
void Initialize ();
private:
void Draw ();
HWND hWnd;
friend LRESULT CALLBACK RandomProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
};
After the hWnd is initialized and created by Initialize (), it will send messages such as WM_LBUTTONDOWN to RandomProc. Once a message is received, I would like RandomProc to use Draw () to redraw the window of the class Random.
The thing is, I will have multiple Random variables, and each will have a window. All the windows will send their messages to RandomProc, and will want it to redraw the corresponding windows of the hWnd. I do not know which random variable sent the message based upon the hWnd parameter nor the msg/wParam/lParam, and so cannot access the appropriate Draw () function and cannot redraw the correct window.
Is there a way to pass a pointer to the class of the window to the Procedure every time a message is sent or is there another way to access the Random class whose hWnd sent the message?
Aren't you looking for the GetWindowLongPtr/SetWindowLongPtr functions?
This function assigns/retrieves arbitrary pointer to/from the window handle. You might assign the pointer to your Random class instance to each window you create. In the RandomProc you just use the GetWindowLongPtr and cast the pointer to Random*.
As Chris says in a comment, use the GWLP_USERDATA attribute to assign the pointer.

sending lparam as a pointer to class, and use it in WndProc()

i have this abstract code :
i want to use lParam (last parameter) in CreateWindowEx() to save a pointer to a class thats declared in the begining of main - SaveArr. then, i want to use it in the function WndProc.
in the begining i did a global array, and then i could use it anywhere, but its not so "clever" as far as c++ concern, so im trying to upgrade it a bit.
class Samples
{
int arr[ITERATIONS+1];
int index;
...
}
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
Samples * SaveArr;
...
hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
ClsName,
WindowCaption,
WS_OVERLAPPEDWINDOW,
INITIAL_WIN_LOCAT_X,
INITIAL_WIN_LOCAT_Y,
WIN_WIDTH,
WIN_HIGHT,
NULL,
NULL,
hInstance,
NULL); //here i want to pass SaveArr, so that i can use it in the WndProc(...) function
...
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
... //here i would like to use lParam as the class pointer, meaning using the
SaveArr declared in the main function.
}
}
Adding caller information to the window:
m_window = CreateWindow(..., this);
Similar for the extended CreateWindowEx.
Obtaining a pointer to the caller:
template< typename CallerT >
[[nodiscard]]
CallerT* WindowCaller(HWND window, UINT message, LPARAM lParam) noexcept
{
if (message == WM_NCCREATE) [[unlikely]]
{
const auto caller = reinterpret_cast< CallerT* >(
reinterpret_cast< CREATESTRUCT* >(lParam)->lpCreateParams);
// Change the user data of the window for subsequent messages.
::SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast< LONG_PTR >(caller));
return caller;
}
else
{
// Retrieve the user data of the window.
return reinterpret_cast< CallerT* >(
::GetWindowLongPtr(window, GWLP_USERDATA));
}
}
This method needs to be called in your message callback.
Best way would be
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
Samples *savearr = (Samples*)GetWindowLong(hWnd,GWL_USERDATA)
switch(Msg)
{
case WM_CREATE:
SetWindowLong(hWnd, GWL_USERDATA, (LONG)lParam);
break;
}
}
The next time the WndProc is called the value would be in savearr, and can be used.
From the reference:
lpParam [in, optional]
Type: LPVOID
Pointer to a value to be passed to the window through the
CREATESTRUCT structure (lpCreateParams member) pointed to by the
lParam param of the WM_CREATE message. This message is sent to the
created window by this function before it returns.
If an application calls CreateWindow to create a MDI client
window, lpParam should point to a CLIENTCREATESTRUCT structure. If an
MDI client window calls CreateWindow to create an MDI child window,
lpParam should point to a MDICREATESTRUCT structure. lpParam may be
NULL if no additional data is needed.
You're expecting the lParam to be always passed to WndProc, but it is only passed with WM_CREATE.
Note, that even then it's not passed directly, but rather through a structure which is the actual lParam for WM_CREATE.
Your only chance to read the lParam is during WM_CREATE. If you want to keep using the value later then you must store it somewhere. Maybe as a static of the WndProc or otherwise assign it to something else which is going to be scoped.
Why all the insistence on using that last, lpParam value set to X, then catching it on WM_CREATE (through all that indirect struct stuff, no less!) and then setting GWL_USERDATA?!
Why not cut to the chase and do this:
HWND H=CreateWindow(.....)
SetWindowLong(H,GWL_USERDATA,X)
In other words, just put X there directly, yourself, right after the window creation statement.
In my tests it works, and so long as you test the window handle against some list of known handles, you can prevent some errant message picked up by your program, and prevent inappropriate use of something else's userdata.

Win32: Modal dialog not returning focus

I'm writing a win32 wrapper classes, mainly to learn more about win32 programming.
To get around the problem of c-style callbacks, the following method stores/retrieves the pointer using SetWindowLong/GetWindowLong and passes it to the actual winproc.
LRESULT CALLBACK WinClass::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// On window creation, WindowProc receives lParam as a LPCREATESTRUCT
// Store *this* pointer as long in GWL_USERDATA
if (msg == WM_NCCREATE)
::SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<long>(reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams));
// Retrieve the pointer
WinClass *wnd = reinterpret_cast<WinClass*>(::GetWindowLongPtr(hwnd, GWL_USERDATA));
// Call the actual winproc function
if (wnd)
return wnd->WndProc(hwnd, msg, wParam, lParam);
// Default to DefWindowProc message handler function
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
Winclass is the class wrapping the main window created by CreateWindowEx. The same WindowProc function is part of the MDlgClass wrapping the modal dialog. I'm calling the dialog like this
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(id), hwnd, DialogProc);
If I pass NULL as the hWndParent, the dialog works okay as a modeless dialog but if I pass hwnd, the handle to the main window as hWndParent the dialog works correctly as a modal dialog. However when i close the dialog it doesnt pass control back to the main parent window? Debugging in Visual Studio shows its hanging in the message pump in WinMain.
I thought of using a hashmap to map the pointers but I'd rather do it using GetWindowLong etc. Is this possible? I've tried storing the dialog pointer in DWL_USER but it doesnt help.
Any help would be appreciated, I'm still getting my head around Win32.
EDIT: I'm destroying the dialog using EndDialog
EDIT: I'm storing the pointer in the GWL_USERDATA region of the main window, which is not used by windows and I'm only modifying it in WinClass::WindowProc when the window is first created. If I don't instantiate a dialog class, I know the pointer is being accessed correctly since the application responds to menu commands processed via WindowProc and WM_COMMAND.
You can't use a WindowProc as a DialogProc. Window Procedures call DefWindowProc when they don't handle a message and return a meaningful result when they do.
Dialog Procedures return FALSE when they don't process a message, return TRUE when they DO, (except when they handle WM_INITDIALOG) and, if they have a meaningful result that they need to return from the outer window procedure, that is placed in DWL_MSGRESULT.
When you call the modal dialog box function, DialogBox, it enters a message pumping loop - which does dispatch messages to all windows in the thread so all windows continue to paint and process input - when the dialog is closed (using EndDialog), the DialogBox procedure should return.
When making a class to wrap dialog boxes, the usual method is to pass the 'this' pointer to One of the DialogBoxParam functions - which can be directly extracted from the WM_INITDIALOG message.
How are you closing the window? Are you using DestroyWindow? While the child window is live, the parent window will be disabled.
You're saving a pointer to lpCreateParams. Is it possible that the associated memory block is being freed or otherwise destroyed?
//static method
BOOL CALLBACK WinClass::DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Store *this* pointer as long in GWL_USERDATA
if (msg == WM_INITDIALOG)
{
::SetWindowLongPtr(hwnd, DWLP_USER, reinterpret_cast(lParam));
m_hWnd = hWnd; // I assume you really don't want to keep passing the hwnd around
}
WinClass* wnd = reinterpret_cast(::GetWindowLongPtr(hwnd, GWL_USERDATA));
if (wnd)
return wnd->DlgProcImpl(umsg, wParam, lParam);
return FALSE;
}
BOOL WinClass::DlgProcImpl(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// your code goes here
return FALSE;
}
return FALSE;
}
INT_PTR WinClass:DoModalDialog(HINSTANCE hInst, HWND hwndParent, LPCTSTR template)
{
return ::DialogBoxParam(hInst, template, hwndParent, WinClass::DlgProc, this);
}