I\'m interested to know the best / common way of storing a this
pointer for use in the WndProc
. I know of several approaches, but each as I underst
While using the SetWindowLongPtr and GetWindowLongPtr to access the GWL_USERDATA might sound like a good idea, I would strongly recommend not using this approach.
This is the exactly the approached used by the Zeus editor and in recent years it has caused nothing but pain.
I think what happens is third party windows messages are sent to Zeus that also have their GWL_USERDATA value set. One application in particular was a Microsoft tool that provied an alternative way to enter Asian characters in any windows application (i.e. some sort of software keyboard utility).
The problem is Zeus always assumes the GWL_USERDATA data was set by it and tries to use the data as a this pointer, which then results in a crash.
If I was to do it all again with, what I know now, I would go for a cached hash lookup approach where the window handle is used as the key.
You should use GetWindowLongPtr()
/SetWindowLongPtr()
(or the deprecated GetWindowLong()
/SetWindowLong()
). They are fast and do exactly what you want to do. The only tricky part is figuring out when to call SetWindowLongPtr()
- You need to do this when the first window message is sent, which is WM_NCCREATE
.
See this article for sample code and a more in-depth discussion.
Thread-local storage is a bad idea, since you may have multiple windows running in one thread.
A hash map would also work, but computing the hash function for every window message (and there are a LOT) can get expensive.
I'm not sure how you mean to use thunks; how are you passing around the thunks?
I recommend setting a thread_local
variable just before calling CreateWindow
, and reading it in your WindowProc
to find out the this
variable (I presume you have control over WindowProc
).
This way you'll have the this
/HWND
association on the very first message sent to you window.
With the other approaches suggested here chances are you'll miss on some messages: those sent before WM_CREATE
/ WM_NCCREATE
/ WM_GETMINMAXINFO
.
class Window
{
// ...
static thread_local Window* _windowBeingCreated;
static thread_local std::unordered_map<HWND, Window*> _hwndMap;
// ...
HWND _hwnd;
// ...
// all error checking omitted
// ...
void Create (HWND parentHWnd, UINT nID, HINSTANCE hinstance)
{
// ...
_windowBeingCreated = this;
::CreateWindow (YourWndClassName, L"", WS_CHILD | WS_VISIBLE, x, y, w, h, parentHWnd, (HMENU) nID, hinstance, NULL);
}
static LRESULT CALLBACK Window::WindowProcStatic (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
Window* _this;
if (_windowBeingCreated != nullptr)
{
_hwndMap[hwnd] = _windowBeingCreated;
_windowBeingCreated->_hwnd = hwnd;
_this = _windowBeingCreated;
windowBeingCreated = NULL;
}
else
{
auto existing = _hwndMap.find (hwnd);
_this = existing->second;
}
return _this->WindowProc (msg, wparam, lparam);
}
LRESULT Window::WindowProc (UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
// ....
In the past I've used the lpParam parameter of CreateWindowEx
:
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.
The trick here is to have a static
std::map
of HWND to class instance pointers. Its possible that the std::map::find
might be more performant than the SetWindowLongPtr
method. Its certainly easier to write test code using this method though.
Btw if you are using a win32 dialog then you'll need to use the DialogBoxParam
function.