I have read that I should never send WM_PAINT
manually and should call InvalidateRect
instead but didn't found anything about why not, however. So why not?
update works with InvalidateRect
but not with SendMessage(WM_PAINT)
LRESULT CALLBACK window_proc(HWND wnd, UINT msg, WPARAM w_param, LPARAM l_param)
{
switch (msg)
{
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(wnd, &ps);
Polyline(..);
EndPaint(wnd, &ps);
return 0;
case WM_USER:
// SendMessage(wnd, WM_PAINT, NULL, NULL);
// InvalidateRect(wnd, NULL, FALSE);
return 0;
}
}
Because
WM_PAINT
is not a real message.Think of it like each window has a structure storing an "invalid region", that is, "this part of the window on the screen is not up to date anymore and need to be repainted".
That invalid region is modified by the window manager itself (window is resized, uncovered, etc ), or by calls to
InvalidateRect
,ValidateRect
,EndPaint
and such.Now, here is a crude mock-up of how
GetMessage
handles this :tl;dr:
WM_PAINT
is meant to be received, not sent.You don't have any information about other program's windows uncovering your windows. Only the operating system has this information. So you don't really know all the time when or where your window needs to be repainted. WM_PAINT and BeginPaint provide this missing information.
Official docs for
WM_PAINT
state that you shouldn't in the very first sentence of the remarks section. Seriously, that should be enough of a reason not to.As for technical reasons why, I guess this is one of them, taken from
BeginPaint
remarks section:Thus
BeginPaint
might not work correctly if you sendWM_PAINT
manually.There might be more reasons/surprises.
If you want to trigger an immediate repaint, the correct approach is to either:
Use
InvalidateRect()
followed byUpdateWindow()
.Use
RedrawWindow()
.Those will trigger a new
WM_PAINT
message to be generated.