Window message: Different between WM_CREATE and WM_NCCREATE? - c++

I tried to create button (child window) inside WM_NCCREATE message, and its position seemed to be created respected to screen coordinates, rather than client coordinates. At first, I thought WM_CREATE and WM_NCCREATE provide us the same handle to window, but this seem to be untrue. Therefore, can anyone explain me the differences between WM_CREATE and WM_NCCREATE messages? Also what are the differences between handle to window in WM_CREATE and in WM_NCCREATE?

WM_NCCREATE is an example of an arms race in progress. It seems to have been introduced to serve a need where DefWindowProc (or the base window proc of a commonly subclassed window) needed to perform some initialization perhaps before WM_CREATE was processed (or to make up for the fact that many window implementations handle WM_CREATE directly and return TRUE rather than passing it on to DefWindowProc).
WM_NCCREATE therefore is the message you should respond to if you are implementing a default window procedure, that needs to perform initialization before the users window proc handles the WM_CREATE message. WM_NCCREATE also MUST be passed on to the appropriate DefWindowProc, probably before you do your own processing as some lower level aspects of the window are clearly in an uninitialized state before WM_NCCREATE is processed.
If trying to guarantee first-look processing is NOT your consideration, then WM_CREATE is the appropriate place to perform your window initialization: All other layers that might have jist-in-time setup via WM_NCCREATE have been done, and the window is in a stable state wrt things like its non client metrics, screen position etc.
Or: If you don't know why you should use WM_NCCREATE over WM_CREATE, then you should not be using WM_NCCREATE.

The WM_NC messages are for the non-client area, i.e. the window border and caption. For your needs you are not interested in these non-client messages.

Per MSDN:
WM_NCCREATE:
Sent prior to the WM_CREATE message
when a window is first created.
Return Value:
If an application processes this
message, it should return TRUE to
continue creation of the window. If
the application returns FALSE, the
CreateWindow or CreateWindowEx
function will return a NULL handle.
WM_CREATE:
Sent when an application requests that
a window be created by calling the
CreateWindowEx or CreateWindow
function. (The message is sent before
the function returns.) The window
procedure of the new window receives
this message after the window is
created, but before the window becomes
visible.
Return Value:
If an application processes this
message, it should return zero to
continue creation of the window. If
the application returns –1, the window
is destroyed and the CreateWindowEx or
CreateWindow function returns a NULL
handle.

This device context includes the window title bar, menu, scroll bars, and frame in addition to the client
area. Applications programs rarely use the GetWindowDC function. If you want to experiment with it,
you should also trap the WM_NCPAINT ("nonclient paint") message, which is the message Windows
uses to draw on the nonclient areas of the window.
from:《Programming Windows Fifth Edition》 -Charles Petzold
So I think it's believable, although MSDN didn't say it。

Not sure why you're creating a button in the WM_NCCREATE -- because the window onto which the button will appear doesn't exist yet, hence (I believe) the destop coords. WM_NCCREATE gets sent to you when the 'non-client' areas of the window are about to be created (non-client areas such as the window's border, title bar, etc.)
Are you needing to put a button on the non-client area? If the answer is no, then why not do the button create inside
WM_CREATE.
If you have to create the button for some reason inside WM_NCCREATE, then why not store the window handle returned by your Createwindow() call. Then, inside your WM_CREATE message handler, grab that button's window handle and do a 'MoveWindow(...)' on it using the app window which you should now have coordinates to when you're in the WM_CREATE message handler.
I believe one of the parameters you can pass to your CreateWindow(...) call to create the button allows you to specify an 'SW_...'
flag, such as 'SW_HIDE' if memory serves me correct. So create but don't show the button in WM_NCCREATE handling if you must, then when WM_CREATE comes quickly afterward, do a 'MoveWindow(....window coords,......SW_SHOW,......) etc.
to position and make visible the button.

Related

Capturing WM_MOUSEMOVE events in parent window

I have a (parent) window in which a child-window is created by a another library (Ogre3d). Window initialization uses the bare Win32 API.
I'd like to capture the mouse input in my parent window, but it seems like I my WM_MOUSEMOVE events are received only by the child-window, which makes sense, since that fills all of my client-area.
Is there a way to capture the WM_MOUSEMOVE messages in the parent window or (unintrusively) redirect those messages from the child-window?
If you can obtain a handle to the window in question you can subclass it.
In a nutshell, you get to register a callback function that gets a crack at all of the messages sent to the sub-classed window.
The linked article should get you where you need to be.

C++ WINAPI: Client area parent dragging with obscuring child tab windows

Attempting to simulate client area based window dragging by returning HTCAPTION under a WM_NCHITTEST (excluding HTCLIENT & appropriate areas) works flawlessly when used with a parent window - however presence of child windows such as tabs placeholder windows, even when set to the extended style WS_EX_TRANSPARENT, cause clicks to fail to pass WM_NCHITTEST messages to the parent window (and attempting to process local WM_NCHITEST messages in a similar fashion produces the expected effect of dragging the child window around the parent rather than the parent itself).
Given that every area in that tab child window appears to be considered to be client area, processing WM_LBUTTONDOWN instead appears to produce the desired effect (see below):
case WM_LBUTTONDOWN: {
SendMessage(mainWnd.hWnd, WM_NCLBUTTONDOWN, HTCAPTION, lParam);
break;
}
Where mainWnd.hWnd is the parent window handle (hWnd is a member of a designed window properties helper class)
Although this produces the desired effect, I'm confused at whether WS_EX_TRANSPARENT is actually meant to allow clicks to pass through to underlying windows, and whether there is a more appropriate solution?
Have you tried returning HTTRANSPARENT from WM_NCHITTEST for the tab control? I think that should propagate the message to the parent window.
WS_EX_TRANSPARENT has to do with how the window is painted afaik.

capture the last WM_SIZE

When I resize my window I want to tell another part of my program that my window has changed size. I read on MSDN that:
WM SIZE Message
The WM SIZE message is sent to a window after its size has changed.
However, I receive the WM_SIZE even when dragging.
I noticed that there is also a WM_SIZING message that is sent when my window is resizing. At the moment I do not see the difference between WM_SIZE and WM_SIZING.
Is there some way I can capture the very last WM_SIZE message, as to not "spam" my program with resize messages?
When you start dragging a window, the system enters a modal move/resize loop; it does not return to your own message loop until the drag action has finished. You are still getting WM_SIZE because it is sent directly to the window procedure, but it does not flow through your own message loop.
At the beginning of such a modal drag action, the system sends WM_ENTERSIZEMOVE to your window procedure. When you release the mouse button, your application will get WM_EXITSIZEMOVE. That is probably the message you want to trigger on.
I have observed this pattern:
WM_ENTERSIZEMOVE
Zero or more groups of:
WM_SIZING
WM_SIZE
WM_EXITSIZEMOVE
WM_SIZING doc says:
lParam
A pointer to a RECT structure with the screen coordinates of the drag
rectangle. To change the size or position of the drag rectangle, an
application must change the members of this structure.
Credit to this blog post: https://billthefarmer.github.io/blog/post/handling-resizing-in-windows/
I have a windows app where I want the app window to maintain it’s
aspect ratio when it is resized. Windows provides two mechanisms for
this, the WM_SIZE message and the WM_SIZING message. The WM_SIZE
message is sent when a window has been resized to allow child windows
to be resized, the WM_SIZING message is sent while the user is
resizing it to allow the size to be adjusted. The messages are handled
in the WindowProc callback function.
Also, WM_SIZING provides:
wParam
The edge of the window that is being sized.
Ideas about capturing the last WM_SIZE message:
On each WM_SIZE, save whatever details you need into a global variable (or user data pointer associated with window handle). On WM_EXITSIZEMOVE, use last WM_SIZE details from global var.
On WM_EXITSIZEMOVE, call GetWindowRect() or GetClientRect() depending upon your needs.
I am sure #1 will work, but I did not test #2.

Constraining window position to desktop working area

I want to allow a user to drag my Win32 window around only inside the working area of the desktop. In other words, they shouldn't be able to have any part of the window extend outside the monitor(s) nor should the window overlap the taskbar.
I'd like to do it in a way that does cause any stuttering. Handling WM_MOVE messages and calling MoveWindow() to reposition the window if it goes off works, but I don't like the flickering effect that's caused by MoveWindow().
I also tried handling WM_MOVING which prevents the need to call MoveWindow() by altering the destination rectangle before the move actually happens. This resolves the flickering problem, but another issue I run into is that the cursor some times gets aways from the window when a drag occurs allowing the user to drag the window around while the cursor is not even inside the window.
How do I constrain my window without running into these issues?
Windows are, ultimately, positioned via the SetWindowPos API.
SetWindowPos starts by validating its parameters by sending the window being sized or moved a WM_WINDOWPOSCHANGING message, and then a WM_WINDOWPOSCHANGED message notifying the window proc of the changed size and/or position.
DefWindowProc handling of these messages is to, in turn, send WM_GETMINMAXINFO and then WM_SIZE or WM_MOVE messages.
Anyway, handle WM_WINDOWPOSCHANGING to filter both user, and code, based attempts to position a window out of bounds.
Keep in mind that users with multi-monitor setups may have a desktop that extends into negative x- and y-coordinates, or that is not rectangular. Also, some users use alternative window managers such as LiteStep, which implement virtual desktops by moving them off-screen; if you try to fight this, your application will break for these users.
You can do this by handling the WM_MOVING message and changing the RECT pointed to by the lParam.
lParam: Pointer to a RECT structure with the current position of the window, in screen coordinates. To change the position of the drag rectangle, an application must change the members of this structure.
you may also want to handle WM_ENTERSIZEMOVE to know when the window is beginning to move, and WM_EXITSIZEMOVE
WM_GETMINMAXINFO is what you seem to be looking for.

Why does a call to BeginPaint() always generate a WM_NCPAINT message?

I'm facing a problem on the Win32 API. I have a program that, when it handles WM_PAINT messages, it calls BeginPaint to clip the region and validate the update region, but the BeginPaint function is always generating a WM_NCPAINT message with the same update region, even if the touched part that needs repainting is only inside the client region.
Do anyone has any clue why this is happening? It's on child windows with the WS_CHILD style.
The MSDN entry for WM_PAINT says:
The function may also send the WM_NCPAINT message to the window procedure if the window frame must be painted and send the WM_ERASEBKGND message if the window background must be erased.
I'm trying to figure out why it is always sending even if the border isn't touched. I test that opening a small Notepad inside the control and minimizing. It doesn't touch the borders of the control, just inside and BeginPaint() generates a WM_NCPAINT.
What happens if you call SetWindowPos and pass SWP_DEFERERASE as argument for the uFlags parameter?
This should prevent generation of the WM_SYNCPAINT message, which would indirectly cause the WM_NCPAINT message to be sent.
I guess the WM_NCPAINT message is always sent with the assumption that the border needs to be repainted as well!