Why is WM_CLOSE not handled automatically by DefDlgProc? - winapi

When I create a dialog using DialogBox, it won't close unless I handle WM_CLOSE in my own DLGPROC function and call EndDialog.
I know that this is by design, but I'm interested why was it chosen for WM_CLOSE not to be handled automatically by DefDlgProc? Are there any good reasons for that?

Ask yourself this:
What would the default handling of WM_CLOSE be? Calling EndDialog? I think EndDialog would only work in very rare situations.
Other suggestions:
validating user input in the dialog, showing errors if input is out-of-range or otherwise not valid, not ending the dialog
closing child windows, releasing resources/memory that child windows of the dialog are using, releasing COM-Objects, basically: cleaning up first and then end the dialog.

Related

No WM_SIZE received when windows resized

I have a fairly simple windows program that created a listview control that should exactly fill the client area. That works at start up, and I think will work if the window is resized --- except the windows NEVER receives any WM_SIZE messages (after the initial one sent on window creation.) I verified this using Spy++x64 as an administrator to ensure I was capturing everything. Spy++ showed the window receiving WM_SIZING, WM_WINPOSCHANGED, WM_NCCALCSIZE, and WM_WINPOSCHANGING (this list isn't in any particular order) but NEVER a WM_SIZE.
This is a 64-bit program, but I don't know why that should matter.
So, is there something I could have done that allows the window to be completely resizable, but prevent Windows from ever generating WM_SIZE messages when that happens? If not, is there something I need to do (that was never needed in the past 30 years) to let Windows know I need to see those WM_SIZE events?
It turns out that the default window procedure generates the WM_SIZE when it processed WM_WINDOWPOSCHANGED and, since I was handling WM_WINDOWPOSCHANGED, no size messages were created. So I forwarded the position changed message to the default handler and the messages are back.
This follows documented behavior. There's a remark in the documentation for the WM_WINDOWPOSCHANGED documentation:
By default, the DefWindowProc function sends the WM_SIZE and WM_MOVE messages to the window. The WM_SIZE and WM_MOVE messages are not sent if an application handles the WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient to perform any move or size change processing during the WM_WINDOWPOSCHANGED message without calling DefWindowProc.
Yup, I'm sorry to admit that I failed to read the documentation for all of the Window messages that I was NOT having problems with when I needed to find out about WM_SIZE. Silly me for not assuming the documentation I needed about WM_SIZE was only to be found under some other message! All it says about the message generation in the WM_SIZE documentation is:
Sent to a window after its size has changed.
There is no mention AT ALL of any dependency on the default processing for a DIFFERENT message to be found. Ergo, the behavior is effectively undocumented, especially since it may be critical information, as it was for me.
Oh well, I give up, this place has become far too much of just smacking people down for asking questions some people think are too easily answered. Try to remember that not everyone has an eidetic memory and access to all of the documentation you have all memorized. Some of us look up the thing we're working on and expect to find the important details about it. THEY ARE NOT PRESENT. Bye!

Winapi: window is "sent to back" when using EnableWindow() function

To prevent users from clicking in my main_window when a MessageBox appears I have used:
EnableWindow(main_window,FALSE);
I got a sample MessageBox:
EnableWindow(main_window,FALSE);
MessageBox(NULL,"some text here","About me",MB_ICONASTERISK);
EnableWindow(main_window,TRUE);
The problem is that when I press "OK" on my MessageBox it closes and my main_window is send to back of all other system windows. Why this is happening?
I tried to put:
SetFocus(main_window);
SetActiveWindow(main_window);
after and before : EnableWindow(main_window,TRUE) the result was strange: it worked 50/50. Guess I do it the way it shouldn't be.
Btw.
Is there a better solution to BLOCK mouse click's on specific window than:
EnableWindow(main_window,FALSE);
Displaying modal UI requires that the modal child is enabled and the owner is disabled. When the modal child is finished the procedure has to be reversed. The code you posted looks like a straight forward way to implement this.
Except, it isn't.
The problem is in between the calls to MessageBox and EnableWindow, code that you did not write. MessageBox returns after the modal child (the message box) has been destroyed. Since this is the window with foreground activiation the window manager then tries to find a new window to activate. There is no owning window, so it starts searching from the top of the Z-order. The first window it finds is yours, but it is still disabled. So the window manager skips it and looks for another window, one that is not disabled. By the time the call to EnableWindow is executed it is too late - the window manager has already concluded that another window should be activated.
The correct order would be to enable the owner prior to destroying the modal UI.
This, however, is only necessary if you have a reason to implement modality yourself. The system provides a standard implementation for modal UI. To make use of it pass a handle to the owning window to calls like MessageBox or CreateDialog (*), and the window manager will do all the heavy lifting for you.
(*): The formal parameter to CreateDialog is unfortunately misnamed as hWndParent. Parent-child and owner-owned relationships are very different (see About Windows).

What is the relationship of CloseWindow and WM_CLOSE

I'm a bit confused currently: Are WM_CLOSE and ::CloseWindow in any way "related" or are for completely different things?
The docs for Closing Windows don't mention the API function CloseWindow at all. Should CloseWindow be really called "MinimizeWindow" or what am I missing?
CloseWindow and WM_CLOSE are completely unrelated. The CloseWindow function is badly named. Its inverse function, OpenWindow is similarly badly named given that it restores windows.
I suspect these names dates back a very long way indeed, probably to Windows version 1 or 2. I'm speculating that what we now call minimize and restore were, back then, called close and open.
The usual way to minimize or restore a window is to call ShowWindow passing SW_MINIMIZE or SW_RESTORE.
I suggest that you forget all about CloseWindow and OpenWindow.
CloseWindow is an unusually poorly named winapi function. It doesn't actually close a window, it just minimizes it. What you possibly meant was DestroyWindow().
WM_CLOSE is normally a message that's generated by default window procedure, in response to the user pressing Alt+F4 or clicking the window's close button. The underlying message is WM_SYSCOMMAND, SC_CLOSE. It can be generated in code as well, like a Window + Close menu item.
You can listen for WM_CLOSE in your window procedure or the MFC message map. The user will expect the window to be closed. So you normally call DestroyWindow(). You don't have to, you might display a message box for example and ask the user if data should be saved. And if he clicks No then you don't call DestroyWindow().
I would say ignore CloseWindow and OpenWindow() for the reasons the previous poster suggested. However, while technically doable, it is considered bad form to handle whether to save or actually destroy a window (ie prompting the user) during WM_DESTROY. Once DestroyWindow is called and the OS sends a WM_DESTROY message, the expectation is to prepare for the destruction of the window. So the workflow should have be like this:
Send a WM_CLOSE to the window handle in question. CloseWindow SHOULD do this, like DestroyWindow sends a WM_DESTROY message or ShowWindow sends a WM_SHOWWINDOW message. But again, since it doesn't, send the WM_CLOSE to the window.
Next, during the processing of the WM_CLOSE message, you can ask the user if they wish to save anything, or if they intend to actually close the window. If they cancel closing the window, simply return ZERO to tell the OS you processed the message. If the user indicates they wish to proceed, THEN you can call DestroyWindow OR simply pass the message to DefWindowProc, which the promptly do the same. Any saving of files should happen during the processing of WM_CLOSE. During the processing of WM_DESTROY, you should delete any dynamically allocated memory associated with the window and free resources. And finally FYI, WM_NCDESTROY is the last message a window receives.

Why we need to call UpdateWindow following ShowWindow?

ShowWindow(g_hWnd, 1);
UpdateWindow(g_hWnd);
I am wondering why we need to call UpdateWindow following the ShowWindow?
It is entirely unnecessary, your window will paint just fine without it.
You'll see a minor benefit from it if your program goes off doing lots of stuff after creating the window but before entering the message loop. The user has something to look at. A splash screen is the more typical approach.
ShowWindow does not repaint the window. The call to UpdateWindow sends WM_PAINT message to the window and thus repainting it.
Normally, the system sends WM_PAINT only if the message queue is empty. Under normal circumstances this is good enough and it actually optimizes out a lot of unnecessary repaint. The messages in the queue often will change application state which can often result in invalidating part of the window, and hence result in yet another painting (so the user sees the new application state). So the repaint just happens after all such messages are handled and the system thinks the new window content will be valid for some time (until yet another message(s) come into the queue).
However if you need to force the WM_PAINT immediately and bypass the logic above, you may force sending WM_PAINT (if there is an invalid region) by calling UpdateWindow().
ShowWindow causes a WM_PAINT message, whereas anything which makes a previously hidden part of a window visible. UpdateWindow does any outstanding paint message(s) to be delivered immediately, waiting until the paint processing is complete until returning. Without the call to UpdateWindow the message isn't received until your program goes into the message loop. This avoids any possible delay in response to the user.

How to subclass Windows Explorer's window

I want to change color of listview of explorer.exe like this
I got the handle of listview window by GetTopWindow function and his family.
To subclass listview window of explorer.exe, I injected my dll code to explorer by following code.
SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInstDll,
dwExplorerListviewThreadId);
My dll is loaed by explorer.exe well.
And I subclassed the window procedure by SetClassLongPtr(for global subclass) in injected code.
SetClassLongPtr returns success but my subclass function(SubclassProc) receives only WM_CREATE WM_DESTROY and WM_MOVE messages. What's wrong? I expected to get WM_NOTIFY and NM_CUSTOMDRAW.
The problem is that this is not a ListView in the first place; it's instead using Microsoft's internal "DirectlUI" framework, which is used in several places in explorer. It doesn't use any of the Common Control messages such as NM_CUSTOMDRAW. There's pretty much no reasonable way to change the colors it uses.
(Also, generally it's best to use SetWindowLongPtr instead of SetClassLongPtr for subclassing a HWND: SetClassLong only changes the underlying template that is used for creating new windows, but may not end up changing any instances that were based on that. And you should not be using the same function - GetMsgProc - for both the hook callback and the subclass proc; they need to handle the message in different ways, the hook callback needs to call CallNextHookEx while the subclass proc needs to call CallWindowProc with the original wndproc. But none of this really matters since the control isn't a ListView in the first place...)
I think and suppose that the OS has special protections for the explorer.exe process, because otherwise it would be an easy target for malicious code or just applications that think they are more important than they actually are (if some people insist on putting back a shortcut on their desktop every time you start the application, imagine what they would do when they had this sort of access to explorer.exe - everything in the shell).
EDIT: I was intrigued by the question and did some more research, I think there is a more mundane reason, see http://blogs.msdn.com/b/oldnewthing/archive/2005/09/07/461912.aspx. (basically: explorer.exe is the window manager so doesn't know about message routing yet when it receives certain messages, which is why they can't be intercepted with message hooks).

Resources