How can I register another win32 message handler - c++

I want to change the message handler for an old legacy app we use but don't have the source for any more. In a dll that we do have the source for I'm wanting to intercept the window messages and then pass them onto the app. Is this possible? I tried something along the lines of:
WNDPROC lpfnWndProc = NULL;
void GetHandler()
{
HINSTANCE hInstance = GetModuleHandle(NULL);
HWND hWnd = GetActiveWindow();
WCHAR lpClassName[1024];
GetClassName(hWnd,lpClassName,1024);
WNDCLASSEX wc;
GetClassInfoEx(hInstance, lpClassName, &wc);
lpfnWndProc = wc.lpfnWndProc;
wc.lpfnWndProc = NewMessageProc;
RegisterClassEx(&wc);
}
However GetActiveWindow fails and just returns NULL. Is there a simpler way to do this. Infact I'd be happy if I could just simply add another message handler.

It is not clear whether you want to subclass specific controls, or all windows of a particular window class.
If you want to subclass specific controls, the section Subclassing Controls in the MSDN describes how to do this, both for ComCtl32.dll version 6 and above, and the legacy procedure of directly replacing a control's window procedure.
If you want to subclass all controls of a particular window class you would have to change the entries stored in the registered window class, using SetClassLongPtr. Note that this will only affect windows subsequently created with that window class. This is a bit of a Catch 22, as you need to have a window handle when calling SetClassLongPtr, limiting the applicability of subclassing a window class.
As for the code you posted, there are a number of issues:
Your call to GetModuleHandle retrieves the wrong HINSTANCE, namely that of the calling application. Since you need the module handle of the module that registers the window class you have to pass the name of the .dll that implements the controls.
Calling GetActiveWindow may or may not return a value, depending on whether or not the calling thread actually does have an active window. In your case it apparently doesn't, so you need another means of retrieving a window handle, such as FindWindowEx.
Your final call to RegisterClassEx doesn't do what you think: It will simply fail, since you cannot re-register a window class with the same name of an existing window class. You need to call SetClassLongPtr instead, as illustrated above.

I'd actually use SetWindowSubclass after getting the HWND of the window you want to modify the behaviour of. SetWindowLong was deprecated as a way to change the WndProc of a window back around the time that CommCtrl.dll version 6 came out. MSDN can tell you all about that particular part of history and its motivation - just look up SetWindowSubclass.
As it stands, assuming the calling thread has an active window, your code will simply create a new window-class with the same attributes as your target window, albeit with a different WndProc - it wont set the wndproc of an existing window.. - (hence my mention of SetWindowLong and SetWindowSubclass)
EDIT: Or at leaast, it would of not for the oversight I made on that point. As pointed-out in a comment below, this call to RegisterClass will actually fail - you can't register the same className more than once.
You should also probably look at the FindWindow function - just give it a NULL lpWindowName, and the (known) class-name of the target window. In the event that the desired window is not the one that's returned, you could use EnumWindows. Simply call GetClassName in the callback function you supply to EnumWindows, subclassing any/all windows whose class-name matches the class-name of the target window.
Once this window has been subclassed, you can consume its messages as you wish, passing them onto the original window-proc as needed.

Related

::PostMessage doesn't work when I am tabbed in to another program

In our Program we have a Dialog from a separate dll open to display infomation. I need to close this dialog when our system timer causes the system to lock.
I send information to the dll by registering a system message in both my MainFrm and EditDisplayDll
SYSTEMLOCK = RegisterWindowMessage("SystemLock");
When I sent the message via
::PostMessage(GetActiveWindow()->m_hWnd,SYSTEMLOCK,0,0);
The message correctly sends to my EditDisplayDll and closes the dialog when the system locks; however, if I alt tab while waiting for the timeout and use another program(firefox, outlook, etc.) the message never correctly calls to the EditDisplayDll. The MainFrm and other windows inside of the MainFrm correctly lockout and hide themselves in either case.
I have tried also using HWND_BROADCAST with PostMessage and SendNotifyMessage. I have also tried to use FindWindow() and FindWindowEx() to specifically call the EditDisplayDll.
I cannot use something like GetDlgItem() because my MainFrm.cpp doesn't have access to this dll.
My decision to use GetActiveWindow() was because I believe it looks to windows specific to my program no matter what window I am in as seen in the imagery in Foreground Vs Active window
Finally, my question is, is there a way to call all Windows in my program no matter what program I am currently in or is there another way I could get access to the specific IDD of the EditDisplayDll in order to send the SYSTEMLOCK message to it?
CWnd *cWndED = FindWindow(_T("EditDisplay"),_T("EditDisplay")); HWND
hwnd = (HWND)cWndED;
You should use win32 API ::FindWindow with proper class, window name. And do not cast CWnd pointer to HWND. Your code should look like:
HWND hWnd = ::FindWindow(_T("ProperClass"), _T("ProperNmae"));
if (hWnd != NULL)
{
::PostMessage(hWnd, YOUR_MESSAGE, ....);
}
I will suggest you to find your Dll window class and name using Spy++ and then try to find that using above method. Remember it's always better to use native API for this kind of tasks.
FindWindow is a good solution if you know both, name of the window and the element.
If you want to get the HWND of your window - no element inside the window -, you can pass as first parameter NULL.
::FindWindow(NULL, _T("WindowName"));
Back to your code: If you are lucky your PostMessage does nothing, otherwise the active window may catch your message. Who knows how/if it is handled in the active window ? Use the PostMessage if you have a valid IsWindow(HWND) from FindWindow or FindWindowEx.
In case you want a CWnd from your HWND take a look at this. (The call may be slow)
HWND hWnd = ::FindWindow(_T("ClassName"), _T("WindowName"));
if (hWnd && IsWindow(hWnd))
{
::PostMessage(hWnd, MESSAGE_TO_BE_SEND, lParam_or_Flags);
}

How can I modify this MFC-based code snippet to use a window class name of my own choice?

I am looking at some MFC/C++ CView object subclass like this:
BOOL CCustomView::CreateView(DWORD dwStyle,
CDocument * pDocument,
CWnd * pParent,
String title)
{
...
CString className = AfxRegisterWndClass(CS_DBLCLKS,
::LoadCursor(NULL, IDC_IBEAM));
return Create(className, title, dwStyle,
rect, pParent, -1, &context);
}
What I don't like about this, although maybe it's normal for MFC application programming, is that the runtime Window Class Name, is not a name of my own choosing. If later, I wanted to find this window, from another Win32 application, and find the window by window class name, I'd have to use the ugly "Afx:123:39843:39843" strings, and actually, I don't know if those window class names can be counted on to not change. I'd rather change the window class to "CCustomView", but still have it have the same behaviours as the window class created above. How do I do that?
There are better ways to solve your problem. The typical protocol I use is:
Register a window message using RegisterWindowMessage in both applications. The message name should contain a GUID to make it unique
In Application A, use
PostMessage(HWND_BROADCAST, registeredMsg, idIWantToFindYou, HWNDofA)
to publish your window handle to all top level windows. since there's more than one use for registered messages, and you should limit the number of messages you register, use idIWantTofindYou to distinguish between different commands for your message.
The receiving application(s) B now have the window handle of the sender, and can establish a connection (e.g. by
PostMessage(HWNDofA, registeredMessage, idHereIsMyHWnd, HWNDofB)
The upside of this mechanism is not running into problems with unresponsive programs. However, the "connection" isn't immediate, so you have to change your program flow. Alternatively, you can use EnumWindows and SendMessageTimeout to probe all top-level windows.
If you need to use the window class:
The class name assigned by MFC is only so window classes with the same attributes get reused. I am not aware of any problems with using your own window classes.
So the following should work:
Fill a WNDCLASS or WNDCLASSEX with the required attributes
Use DefWindowProc as WNDPROC (that's what MFC does, MFC's WNDPROC is set when creating the window)
Use AfxRegisterClass or RegisterClass to register the window class. AfxRegisterClass checks if the class is already registered and if the class is registered from a DLL, it will unregister the class when the DLL is unloaded. Otherwise they are roughly equivalent.

Getting handle of current window to GetWindowText?

I want to display the Title of the Dialog box:
HWND hWnd = ::GetActiveWindow();
char cc[101];
::GetWindowText(hWnd,cc,100);
MessageBox(cc);
but the result yields a blank "".
not sure what is wrong??
According to MSDN:
Retrieves the window handle to the active window attached to the calling thread's message queue.
This means that if the thread which you are calling the function from doesn't own any window, the function will fail.
You probably want GetForegroundWindow instead.
This may coming a bit late but anyway. If you want to get the current (active) window on the system at any time, the best approach is by using a procedure implemented in a DLL, then installing a global hook that call this procedure.
The following resources are quite helpful:
Creating and using your DLL
An overview on Hooks

How to get HWND in win32?

Is there way to get the HWND handler of my window?
I'm using win32.
You could call GetActiveWindow to get the active control in your application, then repeatedly call GetParent on the returned handle until it returns NULL. The last valid handle you get should be the handle of your main window.
The easier way as someone else said is to store the returned value from CreateWindow somewhere.
It's probably good to understand why there is no simple way. It all boils down to "which window?". You'll likely have multiple windows visible on your screen, right now. For instance, the taskbar at the bottom of your screen is a window. even your own app typically has more than one. For instance, the "File Save" dialog is a window. Even the simple MessageBox is a window.
So, how do you identify which window you're talking about? The common answer is that you identify them by their HWND. So, to get the position of the "File Save" dialog window, you ask for the position associated with that HWND. Obviously, you can get any property that way, except the HWND itself ! It makes sense to ask the X/Y position of HWND(0x5e21), but it's stupid to ask which HWND belongs to HWND(0x5e21).
Now, it may happen that you have another more-or-less unique property and you want to get the HWND from that. For instance, you may have an X/Y position. In that case, WindowFromPoint(xy) will return the HWND at that position.
But the most common case is that you need to react to a Windows message for your window. In that case, you get the HWND of your window as the first argument of your WindowProc().
So, unless you tell us what unique information you do have, we can't tell you how to find the matching HWND.
Didn't you create your window via CreateWindow() or CreateWindowEx()? The CreateWindowEx() function and the CreateWindow() function both return the HWND of the newly created window.
Also, the operating system passes you the HWND of your window(s) via your window procedure. It's not a function that you call; it's a function that the operating system calls to let your application do any processing that's needed.

Best method for storing this pointer for use in WndProc

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 understand it have their own drawbacks. My questions are:
What different ways are there of producing this kind of code:
CWindow::WndProc(UINT msg, WPARAM wParam, LPARAM)
{
this->DoSomething();
}
I can think of Thunks, HashMaps, Thread Local Storage and the Window User Data struct.
What are the pros / cons of each of these approaches?
Points awarded for code examples and recommendations.
This is purely for curiosities sake. After using MFC I've just been wondering how that works and then got to thinking about ATL etc.
Edit: What is the earliest place I can validly use the HWND in the window proc? It is documented as WM_NCCREATE - but if you actually experiment, that's not the first message to be sent to a window.
Edit: ATL uses a thunk for accessing the this pointer. MFC uses a hashtable lookup of HWNDs.
This question has many duplicates and almost-duplicates on SO, yet almost none of the answers I've seen explore the pitfalls of their chosen solutions.
There are several ways how to associate an arbitrary data pointer with a window, and there are 2 different situations to consider. Depending on the situation, the possibilities are different.
Situation 1 is when you are authoring the window class. This means you are implementing the WNDPROC, and it is your intention that other people use your window class in their applications. You generally do not know who will use your window class, and for what.
Situation 2 is when you are using a window class that already exists in your own application. In general, you do not have access to the window class source code, and you cannot modify it.
I'm assuming that the problem isn't getting the data pointer into the WNDPROC initially (that would just be the through the CREATESTRUCT with the lpParam parameter in CreateWindow[ExW]), but rather, how to store it for subsequent calls.
Method 1: cbWndExtra
When Windows creates an instance of a window, it internally allocates a WND struct. This struct has a certain size, contains all sorts of window-related things, like its position, its window class, and its current WNDPROC. At the end of this struct, Windows optionally allocates a number of additional bytes that belong to the struct. The number is specified in WNDCLASSEX.cbWndExtra, which is used in RegisterWindowClassEx.
This implies that this method can only be used if you are the person who registers the window class, i.e. you are authoring the window class.
Applications cannot directly access the WND struct. Instead, use GetWindowLong[Ptr]. Non-negative indices access memory inside the extra bytes at the end of the struct. "0" will access the first extra bytes.
This is a clean, and fast way of doing it, if you are authoring the window class. Most Windows internal controls seem to use this method.
Unfortunately, this method does not play so well with dialogs (DialogBox family). You would have a dialog window class in addition to providing the dialog template, which can become cumbersome to maintain (unless you need to do so for other reasons anyway). If you do want to use it with dialogs, you must specify the window class name in the dialog template, make sure this window class is registered before showing the dialog, and you need to implement a WNDPROC for the dialog (or use DefDlgProc). Furthermore, all dialogs already reserve a certain amount of bytes in cbWndExtra for the dialog manager to function properly. The number of extra bytes needed is the DLGWINDOWEXTRA constant. This means your stuff needs to come after the extra bytes which are already reserved by the dialog. Offset all accesses to the extra memory by DLGWINDOWEXTRA (including the value of cbWndExtra which you specify in your window class).
See also below for an extra method exclusive to dialogs.
Method 2: GWLP_USERDATA
The aforementioned WND struct happens to contain one pointer-sized field, which is not used by the system. It is accessed using GetWindowLongPtr with a negative index (namely, GWLP_USERDATA). A negative index will access fields inside the WND structure. Note that according to this, the negative indices do not seem to represent memory offsets, but are arbitrary.
The problem with GWLP_USERDATA is that it is not clear, and it has not been clear in the past, what exactly the purpose of this field is, and hence, who the owner of this field is. See also this question. The general consensus is that there is no consensus. It is likely that GWLP_USERDATA was meant to be used by users of the window, and not authors of the window class. This implies that using it inside of the WNDPROC is strictly incorrect, as the WNDPROC is always provided by the window class author.
I am personally convinced that this is the intention of the engineers that came up with GWLP_USERDATA simply because if it is true, then the API as a whole is sound, extensible, and future-proof. But if it is not true, then the API is neither of those, and it would be redundant with cbWndExtra.
All standard windows controls that I am aware of (e.g. BUTTON, EDIT, etc.) adhere to this and do not use GWLP_USERDATA internally, leaving it free for the window which uses these controls. The problem is that there are WAY too many examples, including on MSDN and on SO, which break this rule and use GWLP_USERDATA for implementation of the window class. This effectively takes away the cleanest and simplest method for a control user to associate a context pointer with it, simply because way too many people are doing it "wrong" (according to my definition of "wrong"). At worst, the user code does not know that GWLP_USERDATA is occupied, and may overwrite it, which would likely crash the application.
Because of this longstanding dispute about the ownership of GWLP_USERDATA, it is not generally safe to use it. If you are authoring a window class, you probably never should have used it anyway. If you are using a window, you should only do so if you are certain that it is not used by the window class.
Method 3: SetProp
The SetProp family of functions implements access to a property table. Each window has its own, independent properties. The key of this table is a string at API surface level, but internally it is really an ATOM.
SetProp can be used by window class authors, and window users, and it has issues too, but they are different from GWLP_USERDATA. You must make sure that the strings used as the property keys do not collide. The window user may not necessarily know what strings the window class author is using internally. Even though conflicts are unlikely, you can avoid them entirely by using a GUID as string, for example. As is evident when looking at the contents of the global ATOM table, many programs use GUIDs this way.
SetProp must be used with care. Most resources do not explain the pitfalls of this function. Internally, it uses GlobalAddAtom. This has several implications, which need to be considered when using this function:
When calling SetProp (or any other API that uses the global ATOM table), instead of a string, you can use an ATOM, which you get when you register a new string GlobalAddAtom. An ATOM is just an integer which refers to one entry in the ATOM table. This will improve performance; SetProp internally always uses ATOMs as property keys, never strings. Passing a string causes SetProp and similar functions to internally search the ATOM table for a match first. Passing an ATOM directly skips searching the string in the global atom table.
The number of possible string atoms in the global atom table is limited to 16384, system-wide. This is because atoms are 16-bit uints ranging from 0xC000 to 0xFFFF (all values below 0xC000 are pseudo-atoms pointing to fixed strings (which are perfectly fine to use, but you cannot guarantee that nobody else is using them)). It is a bad idea to use many different property names, let alone if those names are dynamically generated at runtime. Instead, you can use a single property to store a pointer to a structure that contains all the data you need.
If you are using a GUID, it is safe to use the same GUID for every window you are working with, even across different software projects, since every window has its own properties. This way, all of your software will only use up at most two entries in the global atom table (you'll need at most one GUID as a window class author, and at most one GUID as a window class user). In fact, it might make sense to define two de-facto standard GUIDs everyone can use for their context pointers (realistically not going to happen).
Because properties use GlobalAddAtom, you must make sure that the atoms are unregistered. Global atoms are not cleaned up when the process exists and will clog up the global atom table until the operating system is restarted. To do this, you must make sure that RemoveProp is called. A good place for this is usually WM_NCDESTROY.
Global atoms are reference-counted. This implies that the counter can overflow at some point. To protect against overflows, once the reference count of an atom reaches 65536, the atom will stay in the atom table forever, and no amount of GlobalDeleteAtom can get rid of it. The operating system must be restarted to free the atom table in this case.
Avoid having many different atom names if you want to use SetProp. Other than that, SetProp/GetProp is a very clean and defensive approach. The dangers of atom leaks could be greatly mitigated if developers agreed upon using the same 2 atom names for all windows, but that is not going to happen.
Method 4: SetWindowSubclass
SetWindowSubclass is meant to allow overriding the WNDPROC of a specific window, so that you can handle some messages in your own callback, and delegate the rest of the messages to the original WNDPROC. For example, this can be used to listen for specific key combinations in an EDIT control, while leaving the rest of the messages to its original implementation.
A convenient side effect of SetWindowSubclass is that the new, replacement WNDPROC is not actually a WNDPROC, but a SUBCLASSPROC.
SUBCLASSPROC has 2 additional parameters, one of them is DWORD_PTR dwRefData. This is arbitrary pointer-sized data. The data comes from you, through the last parameter to SetWindowSubclass. The data is then passed to every invocation of the replacement SUBCLASSPROC. If only every WNDPROC had this parameter, then we wouldn't be in this horrible situation!
This method only helps the window class author.(1) During the initial creation of the window (e.g. WM_CREATE), the window subclasses itself (it can allocate memory for the dwRefData right there if that's appropriate). Deallocation probably best in WM_NCDESTROY. The rest of the code that would normally go in WNDPROC is moved to the replacement SUBCLASSPROC instead.
It can even be used in a dialog's own WM_INITDIALOG message. If the dialog is shown with DialogParamW, the last parameter can be used as dwRefData in a SetWindowSubclass call in the WM_INITDIALOG message. Then, all the rest of the dialog logic goes in the new SUBCLASSPROC, which will receive this dwRefData for every message. Note that this changes semantics slightly. You are now writing at the level of the dialog's window procedure, not the dialog procedure.
Internally, SetWindowSubclass uses a property (using SetProp) whose atom name is UxSubclassInfo. Every instance of SetWindowSubclass uses this name, so it will already be in the global atom table on practically any system. It replaces the window's original WNDPROC with a WNDPROC called MasterSubclassProc. That function uses the data in the UxSubclassInfo property to get the dwRefData and call all registered SUBCLASSPROC functions. This also implies that you should probably not use UxSubclassInfo as your own property name for anything.
Method 5: Thunk
A thunk is a small function whose machine code is dynamically generated at run-time in memory. Its purpose is to call another function, but with additional parameters that seem to magically come out of nowhere.
This would let you define a function which is like WNDPROC, but it has one additional parameter. This parameter could be the equivalent of a "this" pointer. Then, when creating the window, you replace the original stub WNDPROC with a thunk that calls the real, pseudo-WNDPROC with an additional parameter.
The way this works is that when the thunk is created, it generates machine code in memory for a load instruction, loading the value of the extra parameter as a constant, and then a jump instruction to the address of the function which would normally require an additional parameter. The thunk itself can then be called as if it were a regular WNDPROC.
This method can be used by window class authors and is extremely fast. However, the implementation is not trivial. The AtlThunk family of functions implements this, but with a quirk. It does not add an extra parameter. Instead, it replaces the HWND parameter of WNDPROC with your arbitrary piece of data (pointer-sized). However, that is not a big problem since your arbitrary data may be a pointer to a struct containing the HWND of the window.
Similarly to the SetWindowSubclass method, you would create the thunk during window creation, using an arbitrary data pointer. Then, replace the window's WNDPROC with the thunk. All the real work goes in the new, pseudo-WNDPROC which is targeted by the thunk.
Thunks do not mess with the global atom table at all, and there are no string uniqueness considerations either. However, like everything else that is allocated in heap memory, they must be freed, and after that, the thunk may no longer be called. Since WM_NCDESTROY is the last message a window receives, this is the place to do that. Otherwise, you must make sure to reinstall the original WNDPROC when freeing the thunk.
Note that this method of smuggling a "this" pointer into a callback function is practically ubiquitous in many ecosystems, including C# interop with native C functions.
Method 6: Global lookup table
No long explanation needed. In your application, implement a global table where you store HWNDs as keys and context data as values. You are responsible for cleaning up the table, and, if needed, to make it sufficiently fast.
Window class authors can use private tables for their implementations, and window users can use their own tables to store application-specific information. There are no concerns about atoms or string uniqueness.
Bottom line
These methods work if you are the Window Class Author:
cbWndExtra, (GWLP_USERDATA), SetProp, SetWindowSubclass, Thunk, Global lookup table.
Window Class Author means that you are writing the WNDPROC function. For example, you may be implementing a custom picture box control, which allows the user to pan and zoom. You may need additional data to store pan/zoom data (e.g. as a 2D transformation matrix), so that you can implement your WM_PAINT code correctly.
Recommendation: Avoid GWLP_USERDATA because the user code may rely on it; use cbWndExtra if possible.
These methods work if you are the Window User:
GWLP_USERDATA, SetProp, Global lookup table.
Window User means you are creating one or more of the windows and use them in your own application. For example, you may be creating a variable number of buttons dynamically, and each of them is associated with a different piece of data that is relevant when it is being clicked.
Recommendation: Use GWLP_USERDATA if it's a standard Windows control, or you are sure that the control doesn't use it internally. Otherwise, SetProp.
Extra mention when using dialogs
Dialogs, by default, use a window class that has cbWndExtra set to DLGWINDOWEXTRA. It is possible to define your own window class for a dialog, where you allocate, say, DLGWINDOWEXTRA + sizeof(void*), and then access GetWindowLongPtrW(hDlg, DLGWINDOWEXTRA). But while doing so you will find yourself having to answer questions you won't like. For example, which WNDPROC do you use (answer: you can use DefDlgProc), or which class styles do you use (the default dialogs happen to use CS_SAVEBITS | CS_DBLCLKS, but good luck finding an authoritative reference).
Within the DLGWINDOEXTRA bytes, dialogs happen to reserve a pointer-sized field, which can be accessed using GetWindowLongPtr with index DWLP_USER. This is kind of an additional GWLP_USERDATA, and, in theory, has the same problems. In practice I have only ever seen this used inside the DLGPROC which ends up being passed to DialogBox[Param]. After all, the window user still has GWLP_USERDATA. So it is probably safe to use for the window class implementation in practically every situation.
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.
In your constructor, call CreateWindowEx with "this" as the lpParam argument.
Then, on WM_NCCREATE, call the following code:
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) ((CREATESTRUCT*)lParam)->lpCreateParams);
SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
Then, at the top of your window procedure you could do the following:
MyWindowClass *wndptr = (MyWindowClass*) GetWindowLongPtr(hwnd, GWL_USERDATA);
Which allows you to do this:
wndptr->DoSomething();
Of course, you could use the same technique to call something like your function above:
wndptr->WndProc(msg, wparam, lparam);
... which can then use its "this" pointer as expected.
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've used SetProp/GetProp to store a pointer to data with the window itself. I'm not sure how it stacks up to the other items you mentioned.
You can use GetWindowLongPtr and SetWindowLongPtr; use GWLP_USERDATA to attach the pointer to the window. However, if you are writing a custom control I would suggest to use extra window bytes to get the job done. While registering the window class set the WNDCLASS::cbWndExtra to the size of the data like this, wc.cbWndExtra = sizeof(Ctrl*);.
You can get and set the value using GetWindowLongPtr and SetWindowLongPtr with nIndex parameter set to 0. This method can save GWLP_USERDATA for other purposes.
The disadvantage with GetProp and SetProp, there will be a string comparison to get/set a property.
With regard to SetWindowLong() / GetWindowLong() security, according to Microsoft:
The SetWindowLong function fails if
the window specified by the hWnd
parameter does not belong to the same
process as the calling thread.
Unfortunately, until the release of a Security Update on October 12, 2004, Windows would not enforce this rule, allowing an application to set any other application's GWL_USERDATA. Therefore, applications running on unpatched systems are vulnerable to attack through calls to SetWindowLong().
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)
{
// ....
ATL's thunk is the most efficent. the thunk executes once and replaces the callback function for the WINPROC to the classes own message processing member function. subsiquent messages are passed by a direct call to the classes member function by windows. it doesnt get any faster than that.
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.
In order to prevent the problem that occurred in the Zeus editor, simply specify the window in the GetMessage function:
BOOL GetMessage(
LPMSG lpMsg,
HWND hWnd, /*A handle to the window whose messages are to be retrieved.*/
UINT wMsgFilterMin,
UINT wMsgFilterMax
);
NOTE
The window must belong to the current thread.
Easy to read Documentation of the function