WM_CREATE doesn't seem to be processed in child window - c++

I have a window HWND assignWnd that is a child window of the main window. This window doesn't seem to process the WM_CREATE message. For example, here is the WM_CREATE message within my window procedure.
case WM_CREATE:
{
hdc = GetDC(assignWnd);
GetTextMetrics(assignWnd,&tm);
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(assignWnd,hdc);
return 0;
}
The variable cyChar is an int and is declared within the window procedure. It is used later in the WM_PAINT message. Whenever I compile the program, I get a run-time error stating that cyChar is being used before it initialized. If I place the above code within the WM_PAINT message however, the program works as expected.
The obvious problem is that I don't want these calls to be made every time the window is painted. I would like these commands (and others later on) to be processed during the WM_CREATE message.
Any explanation as to why the WM_CREATE message is not being processed in this child window be greatly appreciated.

Presumably cyChar is local to the window procedure. It's not a question of WM_CREATE not being processed, it's that the cyChar you assign in WM_CREATE is not the same cyChar that you use in WM_PAINT. You need to make your variable global or static so that it survives from one call to the window procedure to another.

Related

WinEvent Telling the different objects apart?

I’m listening to global win events for cases such as object destroyed, hidden, shown, created etc…
However I’d like to do certain things based on what that handle (HWND) is. For example did a button just get hidden or destroyed or did a window? Was it a child window that got hidden or parent? The last question regards to seemingly getting an event raised for every object in the parent window when parent window closes which is too much noise. Trying to make sense of what raised the event.
https://learn.microsoft.com/en-us/windows/win32/winauto/what-are-winevents
Windows offers a rich set of functions for interrogating an HWND. For example (error checking omitted for brevity):
did a button just get hidden or destroyed or did a window?
TCHAR classname [32];
GetClassName (hWnd, classname, 32);
if (_tcscmp (classname, __T ("BUTTON")) == 0)
...
Was it a child window that got hidden or parent?
LONG dwStyle = GetWindowLong (hWnd, GWL_STYLE);
if (dwStyle & WS_CHILD)
...
an event [is] raised for every object in the parent window when parent window closes which is too much noise.
I think you are stuck with this. When a parent window is destroyed, it automatically destroys all its children, and each of those will send the corresponding event(s) as a result.

Purpose of WM_CREATE message?

What's the point of having the WM_CREATE message when you can create windows without it.
Calling void CreateWindowA outside of WM_CREATE works so what's the deal?
WM_CREATE message is received by window procedure when window is created and not shown yet. You can prepare initial state. For example, you can create controls (child windows), set default values for controls, etc. If something is wrong, you can cancel creation of window and it will not be shown. In other words, in WM_CREATE you can add custom extension to CreateWindow API.
There are many reasons why an application could want/need to intercept the WM_CREATE message. For example, you may want to check a for a particular condition, and prevent the actual creation if that condition is wrong: you can do this by returning -1 from the WndProc that handles the message (see the documentation):
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.
What's the point of having the WM_CREATE message when you can create
windows without it.
If you don't handle WM_CREATE message explicitly, it is actually handled by system via DefWindowProc(hwnd, uMsg, wParam, lParam); implicitly. So you see the window visible.
WM_CREATE message is sent before the CreateWindowEx or CreateWindow function returns, that give you an opportunity to intervene the result of mentioned function. You can allow (return zero to continue creation of the window) or prevent (returns –1, the window is destroyed) the window to be visible, and how it will looks like.
The WM_NCCREATE and WM_CREATE message are sent before the window
becomes visible. That makes them a good place to initialize your
UI—for example, to determine the initial layout of the window.
Refer to "Managing Application State", "WM_CREATE message".

WinAPI timer callback not called while child window is in focus

I'm working on a 3D editor app using Direct3D and WinAPI. I create a main window, which has a child window that takes up a portion of the main window's client area. This is used by D3D as the render target. I then also create a separate window with CreateWindow, which is built the same way as the main window (i.e a "main window" and an internal child used as a render target), and I make this window a child of the main application window (to ensure that they are minimized/restored/closed together).
The D3D rendering is executed by the render target child windows processing their WM_PAINT messages. To reduce unnecessary overhead, I set the window procedures to only render on WM_PAINT if GetForegroundWindow and GetFocus match the respective window handles. In other words, I only want a window's rendering to be refreshed if it's on top and is focused.
This is my main message loop:
HWND mainWnd;
HWND mainRenderWnd;
HWND childWnd;
HWND childRenderWnd;
// ...
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (!TranslateAccelerator(mainWnd, accel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// Run non-UI code...
}
}
When the main window gets WM_SETFOCUS, I have it set the focus to its render target child window, since I'll want to process inputs there (e.g camera controls):
// ...
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
//...
case WM_SETFOCUS:
{
SetFocus(mainRenderWnd);
return 0;
}
//...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
I do the same in the window procedure for childWnd, setting the focus to childRenderWnd. This window is opened and closed by the user, i.e at any one time it may or may not exist, but when it does and is not minimized, it needs to be the foreground window with focus. Also, to control the framerate for the child window, I use a Timer to refresh it:
static constexpr UINT_PTR RENDER_TIMER_ID = (UINT_PTR)0x200;
void TimerCallback(HWND Arg1, UINT Arg2, UINT_PTR Arg3, DWORD Arg4)
{
if (IsIconic(childWnd) || !(GetFocus() == childRenderWnd))
return;
// Invalidate the render area to make sure it gets redrawn
InvalidateRect(childRenderWnd, nullptr, false);
}
// ...
SetTimer(childWnd, RENDER_TIMER_ID, 16, (TIMERPROC)TimerCallback);
With all this set up, mainWnd and mainRenderWnd seem to work just fine. However, childRenderWnd refuses to have anything rendered to it when it is in the foreground and in focus. While debugging, I found that while this is the case, the timer callback never gets executed, nor does a WM_TIMER message get dispatched to the child window.
On the other hand, the moment I deliberately move focus out of the child window and onto the main window (while keeping both open), the timer message gets sent, and the callback is executed. Another problem is that when I minimize the app while both windows are open, and then restore them both, the render target of neither of the windows is refreshed. Instead, it seems like the focus got "flipped", as I have to click on my child window first, then my main window, and that makes it refresh properly (while the child still refuses to render anything).
What am I missing? I searched for others having problem, e.g an incorrect message pump setup blocking WM_TIMER, but nothing seems to explain what's going on here.
Thanks in advance for the help!
IInspectable and Raymond Chen's comments have helped lead me to the answer. I tried to reproduce the error in a minimal app and finally came upon the source of my trouble. Originally, the main window would just call InvalidateRect in the message loop if there were no messages to be dispatched, effectively redrawing itself every chance it got. This originally did not seem to cause any harm, the scene was rendered just fine, and the window responded to inputs. Once I introduced a second window, however, it must have flooded the message loop with paint messages, making it impossible for any timer messages to get through.
The solution, quite simply, was to give a timer to both windows to set the rate at which they would refresh their D3D render targets. Coupled with focus checks, I can now easily alternate between both windows, with only one refreshing itself at any given time.
The question remains whether the WinAPI timer system is the best choice. My bad code aside, people have mentioned that the timer's messages are low-priority, which might make it a poor choice for framerate control in the long run.
EDIT: I ended up going with IInspectable's suggestion to use a while loop within the main message loop to process to dispatch all messages, and once those are processed, I perform a frame update. This allows me to consistently update all windows and control the frame rate without risking the issue of window messages being stuck.

C++ Window losing focus when child button is pressed

I am trying to get a GUI running for a C++ application but I am having an issue with key press events. Basically, everything works fine, as long as I do not click on any buttons (the main window registers key events), but as soon as I click on a button, the main window loses focus and it no longer captures key events. This might be a stupid question, but I am very new to C++. This is some of the code I am using:
Creation of the main window:
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Application Name", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
540, /* The programs width */
250, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
Creation of one of the buttons:
CreateWindow(TEXT("button"), TEXT("Start"),
WS_VISIBLE | WS_CHILD,
x, y, width, height,
hwnd, (HMENU) 6, NULL, NULL);
I have also noticed that whenever I click on a button, the WM_KILLFOCUS event is fired, which is why I think that this is a focus issue. I have also tried capturing the WM_KILLFOCUS event and then set the focus again with SetActiveWindow but that crashed my program.
Any help would be appreciated :)
It turned out that I was using the wrong function (SetWindowActive). Assaf Levy's answer seemed to complex for me and I thought that there might be another way around this. I managed to find the SetFocus function which gives the focus to any given window by providing it it's handle.
To make it work, what I needed to do was to, once that the the necessary code was executed within the WM_COMMAND block, I called the SetFocus function with the handle of the main window. This gave focus back to the main window and allowed it to receive events.
Note, putting the SetFocus in the WM_KILLFOCUS block will cause the buttons and any other component in it to become unresponsive to events.
This is by design. The main window is a window, but so the button is a window, and only one can have focus at any given time. If you don't want the button to "steal" the focus, add an OnFocus handler (or intercept WM_SETFOCUS) and immediately return focus to the previous window (I believe it's in the WPARAM of WM_SETFOCUS).
An easy hack would be:
hMyButton = CreateWindow("button", ...).
Define a MyButtonProc(HWND, UINT, WPARAM, LPARAM) function.
Call SetWindowLong(hMyButton, GWL_WNDPROC, (LONG)MyButtonProc). Save the value returned by this function in a g_OldButtonProc.
Inside MyButtonProc() catch WM_SETFOCUS, and call SetFocus(hMyMainWindow).
Always return CallWindowProc(h_OldButtonProc, hwnd, msg, ...) at the end of your MyButtonProc() function, unless the message was WM_SETFOCUS.
That will do the trick (tested).
The first answer was partially accurate. Subclassing the button can get "rid" of the "problem"; however handling WM_SETFOCUS be it in parent window, or subclass procedure or BN_SETFOCUS will result in unresponsive UI if you take the focsus from the button.
What you should override in the subclass procedure is WM_LBUTTONUP. Since by the time you release the mouse button you have already clicked the windows button.
Note I think this is utter rubbish for a button to be stealing focus. There should be a style like BS_NO_STEAL_FOCUS, that prevents this. As it is very cumbersome when you want another window to be handling key presses or scrolling.
/** Procedure for subclass.
This procedure is called first for the widget/control.
Unhandled or partially handled message can goes to
original procedure by calling DefSubclassProc(...).
buttonProcEx takes all four params of normal procedure, plus a
param for user defined object.
Note this is what win32 should have done in the first place.
*/
LRESULT CALLBACK buttonProcEx(HWND hwnd,uint msg,WPARAM,LPARAM,DWORD_PTR)
{
if(msg == WM_LBUTTONUP)
{
setFocus(GetParent(hwnd));
return 0; //do not allow default behaviour
}
else return DefSubclassProc(hwnd,msg,wparam,lparam);
}
//Call this after creating your button
SetWindowSubclass((HWND)button,buttonProcEx,0,NULL);
or
struct Content {...}content; //lifetime should be long enough
SetWindowSubclass((HWND)button,buttonProcEx,0,(DWORD_PTR)&content);

The difference between the handle received in the WNDPROC window callback procedure vs the one returned by CreateWindow?

So i was wondering if there is any difference between the handle returned when creating the window using CreateWindow/CreateWindowEx, and the one that is passed as a parameter to the WNDPROC window callback procedure for the concerned window. The context for this question is that I need to use certain API functions that require a handle to the window, so I thought that instead of passing the handle received by the window callback procedures, I could just save a copy of the handle returned by CreateWindow once, and then use it for successive operations, in case both handles are the same.
Regards,
Devjeet
Be careful here, the window procedure is used by multiple instances of a window. The callback is determined by the value of WNDCLASSEX.lpfnWndProc so any CreateWindow/Ex() call that uses the same window class name uses the same callback. If you are doing this to map the callback to a class instance method then you first have to go through a thunk that maps the callback's hwnd argument to a class instance. Making a special case for WM_CREATE of course.
Yes they are the same, that's the whole point of having an HWND. Every window has only one HWND that identifies it within the system and remains valid until DestroyWindow is called with it. It is returned by CreateWindow(EX) and sent in with every message so you can use that as a unique identifier for your window, even if you create several windows of the same class.
But note that some messages are sent to the window procedure even before the CreateFunction returns, so if you use a global variable to hold your HWND:
HWND globalHwnd = NULL;
int main()
{
//...
globalHwnd = CreateWindow(...);
}
LRESULT WndProc(HWND hWnd, ...)
{
assert(hWnd == globalHwnd); //fail!
}
The assertion will fail because several messages such as WM_CREATE are sent before CreateWindow returns, so the global variable is not yet assigned.