Get window icon: GetClassLong VS SendMessage - c++

There are two methods to get window icon if i know his handle:
SendMessage(HWND,0x7F lParam, wParam)
and
GetClassLong(HWND, -14|-34)
But even if I get icon from window with GetClassLong i can't set new icon with SetClassLong, but successfully set new icon with SendMessage.
I need to know: why in some times work second get method, but doen't work first method. And why always from SendMessage(WN_SETICON) and doen't work SetClassLong(HWND, -14|-34, HICON)?

GetClassLong retrieves longs from the window class.
A window class is a blueprint for creating a window of a specified type, not the window itself.
It can contain quite a lot of defaults, like the default icon, and the default small icon.
But a window is only based on it, it can override everything.
Thus, setting the windowclass' icon does not modify any already created classes, you must send a message to the window instead.
And reading the windowclass-data gets you stale data, which might or might not still be relevant to the window.
As an aside, -14|-34 would be GCL_HICON|GCL_HICONSM, or -2, which does not actually make sense.
Explicitly say that you read with both indices one after the other.

-14 is GCL_HICON which is the (optional resource) icon for the class specified when its registered.
A specific window can subsequently specify its own icon (WM_SETICON), subsequent changes to
GCL_HICON won't affect it.
I also doubt you can attempt to pull GCL_HICON | GCL_HICONSM, if they were different what would the value be?

A few additional things:
1) Don't use magic numbers for Windows API constant values. Use WM_GETICON, not 0x7F. Use the GCL_ names as mentioned by the other answers.
2) You switched wParam and lParam in your SendMessage(). wParam comes first.
3) Don't use GetClassLong(); it is not 64-bit safe (and icon handles are pointers). Use GetClassLongPtr() instead. Also substitute GCLP_ for GCL_ in your named constants.
4) Don't try to replace | in your GetClassLong() with || or with an array of indices and expect that to work either. You don't have a choice but to make two calls.

Related

Scrolling text gets negated in static control (C++ WinAPI)

I have some weird control,
CreateWindowEx(!tset&USE?WS_EX_OVERLAPPEDWINDOW:0,
tset&USE?"static":"edit",0,WS_CHILD|WS_VISIBLE|
(!tset&USE?ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL|WS_VSCROLL //when it's edit
:SS_NOTIFY|SS_EDITCONTROL),5,60,390,474,hwnd,HMENU(10),0,0); //when it's static
SetWindowSubclass(GetDlgItem(hwnd,10),tset&USE?BOR:NoMenu,0,0);
whose everything depends on whether themes are active. Here tset is an instance of enum type for bit flags. Everything is fine except scrolling when the control is in static mode. It wasn't receiving WM_MOUSEWHEEL messages, I made it receive them in the following way:
LRESULT CALLBACK BOR(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp,UINT_PTR,DWORD_PTR)
{
if(msg==WM_MOUSEMOVE&&GetDlgCtrlID(hwnd)==10)
{
POINT x{LOWORD(lp),HIWORD(lp)}; ClientToScreen(hwnd,&x);
RECT rc; GetWindowRect(hwnd,&rc); if(PtInRect(&rc,x)&&GetCapture()!=hwnd){SetCapture(hwnd); hwnd3=SetFocus(hwnd);}
if(!PtInRect(&rc,x)&&GetCapture()==hwnd){ReleaseCapture(); SetFocus(hwnd3);} //hwnd3 is an instance of HWND defined in global scope, for giving focus back
}
if(msg==WM_MOUSEWHEEL)
{
RECT rc; GetWindowRect(GetParent(hwnd),&rc);
HRGN x; GetWindowRgn(GetParent(hwnd),x);
ScrollWindowEx(hwnd,0,short(HIWORD(wp)),0,0,x,&rc,SW_INVALIDATE|SW_ERASE);
ShowWindow(GetParent(hwnd),SW_HIDE); ShowWindow(GetParent(hwnd),SW_SHOW);
SetFocus(hwnd);
return 1;
}
return DefSubclassProc(hwnd,msg,wp,lp);
}
Now when mouse enters client area of my control, if it's in static mode, it captures mouse and gets focus. So it intercepts WM_MOUSEWHEEL messages in its subclass callback function and I can scroll my control in the static mode. Here's the exact problem: few milliseconds later, right after its text is scrolled, the window somehow updates itself to its initial unscrolled state. And my efforts get negated. Is it clear why it behaves like this, and how to fix that?
#Edit: Why don't I simply add WS_VSCROLL to its static version? Because it has SS_NOTIFY style and I need it to respond to WM_COMMAND messages somehow else. This is why I tried scrolling it manually.
#Update:
Here in both screenshots the text length is the same. When the control is in edit state, its vertical scrollbar is proper. But when it's in static state the vertical scrollbar doesn't match the actual text length. Besides, the scrollbar is always at the same size and same position independent from text length and it's also unscrollable. Why may be the reason of it behaving like this?
#Update: Thanks to #enhzflep's comment, problem is easily solved some other way.
In the interests of removing the question from the Unanswered list, I'll repeat and expand upon one of the comments.
Looking at the images, we can immediately see a clear difference in the way they're each presented. The left image has each line starting with a different character, while the right image always starts with the same character. They also have very differently sized thumbs in the scroll-bar.
Since the problems seem to be stemming from trying to use two different controls to achieve essentially the same task, I'd suggest always using an edit control. (this is the one that appears to behave nicely, and is the one that doesn't wrap the text)
Rather than use two different types of control, I suggest just toggling the read-only flag. With luck (!) the only difference now will be the way mouse and keyboard messages are handled - hopefully you'll get similar behaviour and appearance with such an approach.
The message I'm thinking of that will achieve this is EM_SETREADONLY.
Although the question has been successful answered, I can't find reference in the Microsoft docs to support my theories, thus used language like 'hopefully'.

which window is on top on the other

I have 2 windows and I want to know which window is on the top of the other?
I tried to test using GetWindowLong and comparing the results but no chance.
LONG wndState1 = ::GetWindowLong(handler1, GWL_EXSTYLE);
LONG wndState2 = ::GetWindowLong(handler2, GWL_EXSTYLE);
both results is equal to 256.
Edited: In the picture below I have the dialog of notepad++ is on top of the FileZilla, How do I Get That by Code.
Is there a trick for that ?
THank you
GetWindowLong is used to retrieve style information for a particular window.
In order to get the top-most window, use
HWND WINAPI GetForegroundWindow(void);
You will still need to know the window handles (HWND) for the processes you're interested in so that you can find out which process owns the foreground window.
Note that this API can only return the window that the user is interacting with (or has interacted with most recently).
UPDATE:
I agree with Remy that there isn't any API to do that. The only way I can think of to actually do that is to install a global hook and intercept certain messages (e.g. WM_ACTIVATE, WM_SETFOCUS and so on). Since you will also retrieve the timestamps for the messages, it should be simple to infer which window is on top of any other window. This will require you to write a dll but this is relatively simple to do. I can't guarantee this will work either though I think it will (I've written a global hook but never used it to find out the z-order of windows)

how to know if my control is visible or not

I have a dialog containing controls (edits, combobox, etc...) some are visible and some or not.
What in a function, I want is looping on all controls and get the last shown control.
I want to add that when creating the dialog, I set the visibility to some controls as SW_HIDE and others to SW_SHOW.
What I did is using the ::IsWindowVisible, but returns false even if I set the visibility to SW_SHOW.
NB: the function is called just after the creation of the dialog, the dialog is not yet visible.
The control is not visible at the time you call the function, because the dialog that contains it is not visible. As per the documentation for the IsWindowVisible function:
If the specified window, its parent window, its parent's parent window, and so forth, have the WS_VISIBLE style, the return value is nonzero. Otherwise, the return value is zero.
This not only explains the behavior that you're seeing, but also suggests a possible solution. If you want to know whether a particular window has the WS_VISIBLE style bit set (and don't care about any of its ancestors), then just retrieve that window's styles and test for the presence of the WS_VISIBLE bit directly. For example:
LONG_PTR wndStyles = GetWindowLongPtr(hwndCtrl, GWL_STYLE);
bool isVisible = (wndStyles & WS_VISIBLE) == WS_VISIBLE;
That said, I don't really understand why you would need to do this. If your dialog-initialization code sets the visibility of controls, then you know what their visibility is. You don't need to retrieve it dynamically at run-time. In other words, this sounds like an X-Y problem. I suspect there is a better way to write your code so that this whole issue is a non-issue.

DeferWindowPos weird behaviour

This happens with all ActiveX controls. If I reposition an ActiveX control with DeferWindowPos
HDWP hdwp = BeginDeferWindowPos(1);
DeferWindowPos(hdwp, m_pActiveX->GetSafeHwnd(), NULL, left, top, width, height, SWP_NOZORDER);
EndDeferWindowPos(hdwp);
it goes there but then moves/resizes to its old rectangle once you click anywhere inside the control. If I use MoveWindow instead
m_pActiveX->MoveWindow(left, top, width, height);
this doesn't happen.
It doesn't happen with any other type of control, only with ActiveX controls, but it happens with all of them. I made a test to confirm this, creating a new ActiveX control project and didn't make any changes, and the problem was still there.
You never got an appropriate answer. I'll try to help out a bit here.
The issue is that MFC hides a lot of the trickiness with hosting an ActiveX control within it's framework. Specifically, if you step into the MoveWindow call, it is not simply a wrapper around the Win32 MoveWindow function. It calls into the OLE Control Container support classes. This basically says, if we have a control site interface, then call COleControlSite::MoveWindow, otherwise call the standard Win32 MoveWindow. The same occurs with several other window functions handled by CWnd etc. For example COleControlSite::SetWindowPos handles hiding/showing the control, then calls COleControlSite::MoveWindow to move it, and then finally calls ::SetWindowPos (with the move/show flags masked out) to handle the rest.
Once within COleControlSite::MoveWindow, you will notice it does several things: it calls SetExtent, updates it's internal m_rect member, and then calls SetObjectRects.
Bypassing these for ActiveX controls using the Win32 API directly (eg via DeferWindowPos) causes some of these crucial steps to be missed. Depending on how your code is layed out, usually you can handle this yourself.
What is this ActiveX control?
Apart from that consider that DeferWindowPos is meant for positioning multiple windows at the same time. The concept being you enter the begin statement, change a bunch of window positions for a new layout, then end to actually move and apply the new positions and sizes.
If you aren't updating multiple windows consider using SetWindowPos instead.
Consider also that you may be getting a message to move, resize, or change the windows position while you are deferring. To prevent this if that is what is happening pass the SWP_NOSENDCHANGING flag in each call to DeferWindowPos so that it is not sent or handle the message and clear all the bits in the WINDOWPOS struct received to prevent unwanted changes.
It is also possible for this call to fail ... are you checking the return value?

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