Calling MoveWindow() with bRepaint set to TRUE - c

The following is taken from the Remarks section of the MoveWindow() documentation:
If the bRepaint parameter is TRUE, the system sends the WM_PAINT
message to the window procedure immediately after moving the window
(that is, the MoveWindow function calls the UpdateWindow function).
So I assumed that when I call MoveWindow() with bRepaint set to TRUE, the window procedure will be called immediately and passed a WM_PAINT message, but this is what my testing shows:
When MoveWindow() is called, the window procedure is called
immediately, but a WM_ERASEBKGND message is passed to it and not a
WM_PAINT message.
The region is still invalid and so when I go back to the message loop and no messages are in the message queue, a WM_PAINT message is sent.
Did I interpret the documentation wrong?
Note: I am talking about calling the MoveWindow() method on the parent window object.
Edit:
This is my test code:
/* Left mouse click on the window to call MoveWindow() */
#include <Windows.h>
HWND hEdit;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_LBUTTONDOWN:
MoveWindow(hWnd, 200, 200, 700, 700, TRUE);
// Do not go back to message loop immediately
Sleep(3000);
break;
case WM_ERASEBKGND:
{
SendMessage(hEdit, WM_CHAR, (WPARAM)'e', 0);
}
break;
case WM_PAINT:
{
SendMessage(hEdit, WM_CHAR, (WPARAM)'p', 0);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "WinClass";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
HWND hWnd = CreateWindowEx(0, "WinClass", "", WS_OVERLAPPEDWINDOW, 261, 172, 594, 384, NULL, NULL, hInstance, NULL);
hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 0, 0, 400, 21, hWnd, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

Did I interpret the documentation wrong?
Basically, yes. You discovered a little fact about MSDN documentation on winapi functions that is very, very important to know. It is not written to be a tutorial. It assumes a basic understanding of how the winapi works, the kind of knowledge you get from reading Petzold's "Programming Windows" book.
That book can teach you that the Windows painting cycle always includes WM_ERASEBKGND. So the background is painted first, what ever you draw on top of it with WM_PAINT is next.
Several reasons why such implementation details are skipped in the MSDN documentation. First off, there is a lot of it and including everything just makes it hard to plow through the article. Next, it is pretty unusual to actually write a message handler for WM_ERASEBKGND. You normally just pass it on to DefWindowProc(). Which uses the WNDCLASSEX.hbrBackground you selected, 99% of the time good enough to get the job done. Note how your window looks screwed up because that's what you did not do. Since you wrote a message handler, it is now your job to take care of it. Easy to do, just call DefWindowProc() yourself.
Finally, MSDN documentation omits details because nailing them down makes it very difficult to ever improve the way Windows works. There's another implementation detail that you can see from your test program. Quite often, calling MoveWindow with bPaint = TRUE does not paint anything at all. Easy to see by moving the window by dragging it with the title bar after you first clicked it. Note how clicking again makes the window jump back but you get neither WM_ERASEBKGND nor WM_PAINT.
That's an optimization at work, making Windows work better. And not mentioned in the MSDN article. If the window didn't move off the screen and back and the size of the window did not change then it can take a shortcut. It simply copies the pixels in the video frame buffer from the old position to the new position. Much more efficient than letting the app repaint everything. If you run with Aero enabled then it is even more optimized, it doesn't have to copy the pixels at all.
Last but not least, while writing code like this to reverse-engineer Windows is pretty educational and recommended, you don't have to. It is much easier to use the Spy++ utility.

What probably happens is that MoveWindow sends WM_ERASEBKGND using SendMessage (which will call the WndProc callback immediately and wait for its processing) but WM_PAINT via PostMessage (which will just put the message in the queue, so it will be processed after sleeping, when DispatchMessage is called).
I don't know if it's just a test or you're really processing something after using MoveWindow which blocks the message queue. If so, then you should consider moving that work to another thread!
Hope it helps.

Did I interpret the documentation wrong?
Yes and no.
No - the documentation is pretty clear on this.
Yes - like Hans Passant said, you just can't rely on such details from MSDN.
There are many WinAPI functions that have "gotchas", undocumented behaviour or expected environment state (incl. timing) and such. It could be that it did behave as specified in some version of Windows. Maybe it still does, under some circumstances.
In practice, MS will test a lot of applications to see if they work after making a change like this. In this case, since you generally process the WM_PAINT "when it happens", and do only painting then, it is easy to see how in most applications this change would not affect the end-user result.
So, always take MSDN as a "general description". Use your own testing and other sources to get the actual behaviour details. Be happy that you're working with the windows-related API, if you ever work some less used APIs, you'll be far worse off (I suffered a lot with the USB / HID related APIs).

i didn't want to override rony's provided test code so i decided to post my alternative, which i think is better suited to debug Windows Message, the only requirement an external tools to view the debug message DebugView. this of course can be replaced by a listbox if some one wishes.
#include <Windows.h>
/*
get DebugView from here https://download.sysinternals.com/files/DebugView.zip
*/
/*___________________________________________________________________________________
*/
void __cdecl DebugPrint(TCHAR *fmt,...){
va_list args = NULL;
va_start(args, fmt);
static TCHAR _buff[512]="";
TCHAR buff[512];
wvsprintf(buff,fmt,args);
va_end(args);
if(lstrcmp(buff,_buff))
OutputDebugString(buff);
lstrcpy(_buff,buff);
return ;
}
/*___________________________________________________________________________________
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_RBUTTONDOWN:
MoveWindow(hWnd, 200, 200, 700, 700, TRUE);
break;
case WM_MOVE:
DebugPrint("WM_MOVE");
break;
case WM_SIZE:
DebugPrint("WM_SIZE");
break;
case WM_ERASEBKGND:
DebugPrint("WM_ERASEBKGND");
break;
case WM_PAINT:
DebugPrint("WM_PAINT");
if(1){
PAINTSTRUCT ps;
TCHAR buff[]="Right mouse click on the window to call MoveWindow()";
HFONT hf=(HFONT)GetStockObject(DEFAULT_GUI_FONT);
HDC hdc = BeginPaint(hWnd, &ps);
hf=SelectObject(hdc,hf);
TextOut(hdc,8,12,buff, sizeof(buff)-sizeof(TCHAR));
hf=SelectObject(hdc,hf);
EndPaint(hWnd, &ps);
}else{
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
/*___________________________________________________________________________________
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
ZeroMemory(&wc,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszClassName = "WinClass";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
HWND hWnd = CreateWindowEx(0, "WinClass", "",WS_OVERLAPPEDWINDOW|WS_VISIBLE,
261, 172, 594, 384, NULL, NULL, hInstance, NULL);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

Related

Strange coordinates in `WM_MOUSEMOVE`

MSDN describes that lParam of WM_MOUSEMOVE is 2 shorts due to it needing to be compatible with virtual coordinates because it acts as a redirected event if capture is set, which is clear. However, negative coordinates are still received under normal circumstances when moving the mouse slightly outside of the window, to be exact a bonus 5 pixels in all directions except up (which is where the caption is, and it'd go to WM_NCMOUSEMOVE instead).
I initially suspected this was to do with the drop shadow technically being part of the window (like with the output of AdjustWindowRectEx's rectangle including it) since you can receive events in some of the shadow, but the values of that don't match up, and clamping the values to the client area's size doesn't feel intended. Where do the bonus 5 pixels (on my system, Windows 10 Education 2004) come from, especially considering that shouldn't even be a part of the client area to my knowledge, and is there a clean/intended way to dodge unwanted values?
I've seen some discussion about the area you can grab to resize the window potentially being related, but my window isn't resizable (doesn't have a thick frame).
Edit: After tinkering, it seems this is related to the window style. Here's a reproducible sample (hopefully on other machines, too):
#include <Windows.h>
#include <stdio.h>
// Remove `^ WS_THICKFRAME` and the bug vanishes!
#define WINDOW_STYLE ((WS_OVERLAPPEDWINDOW | WS_VISIBLE) ^ WS_THICKFRAME)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_MOUSEMOVE) {
POINTS mouse = MAKEPOINTS(lParam);
printf("WM_MOUSEMOVE # x=%hd, y=%hd\n", mouse.x, mouse.y);
} else if (message == WM_DESTROY) {
PostQuitMessage(0);
}
return DefWindowProcW(hWnd, message, wParam, lParam);
}
int main(void) {
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"SO_Example";
wcex.hIconSm = NULL;
RECT windowRect = { 0, 0, 600, 600 };
AdjustWindowRectEx(&windowRect, WINDOW_STYLE, FALSE, 0);
ATOM windowClass = RegisterClassExW(&wcex);
HWND hWnd = CreateWindowExW(
0,
(LPCWSTR)windowClass,
L"hello stackoverflow!",
WINDOW_STYLE,
CW_USEDEFAULT, CW_USEDEFAULT,
windowRect.right - windowRect.left,
windowRect.bottom - windowRect.top,
NULL,
NULL,
GetModuleHandle(NULL),
NULL
);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0) != 0) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
Here's that sample in action: https://i.imgur.com/LfCe9od.mp4
It appears 5 is the border size (1) and the border padding (4) added up (aka the area the user can use to resize the window), so it's safe to discard values outside of the client rectangle, even if it's a little strange that Win32 is reporting events in that area.

Does DestroyWindow() remove the messages for the window from the message queue?

The DestroyWindow() documentation says the following:
The function also destroys the window's menu, flushes the thread
message queue,
Does "flushes the thread message queue" means that it will remove the messages from the message queue for the window that I want to destroy only?
Although the docs are not explicit on this, it does behave as you suggest. Messages posted to the destroyed window are flushed and those for other windows (or posted to the thread) remain in the thread's queue.
The following sample program demonstrates this - if your suggestion is true, no asserts should fire (and in my tests, they don't).
As #HarryJohnston points out in the comments, destroying one window own by a thread does not destroy any other windows the thread owns (except for children and owned windows of the destroyed window), and so it could be quite problematic if their posted messages were removed, effectively for no reason.
#include <windows.h>
#include <assert.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
assert(message != WM_APP + 1);
return DefWindowProc(hWnd, message, wParam, lParam);
}
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEXW wcex{};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = L"msgflushtest";
assert(RegisterClassExW(&wcex));
HWND hWnd = CreateWindowEx(0, L"msgflushtest", nullptr, WS_POPUP, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
HWND_DESKTOP, nullptr, hInstance, nullptr);
assert(hWnd);
assert(PostMessage(hWnd, WM_APP + 1, 0, 0)); // should be flushed
assert(PostThreadMessage(GetCurrentThreadId(), WM_APP + 2, 0, 0)); // should not be flushed
DestroyWindow(hWnd);
MSG msg;
assert(!PeekMessage(&msg, nullptr, WM_APP + 1, WM_APP + 1, PM_REMOVE));
assert(PeekMessage(&msg, nullptr, WM_APP + 2, WM_APP + 2, PM_REMOVE));
return 0;
}

Why am I not getting WM_UPDATEUISTATE?

I have a main window with an edit control and a custom button. When the edit control has focus and I press the Tab key, the button control gets the focus but it never receives WM_UPDATEUISTATE? I'm using IsDialogMessage() in the message loop of main window. Any ideas on why this is happening?
Edit: Why isn't the custom button control receiving WM_UPDATEUISTATE?
Edit: IsDialogMessage() is not sending WM_UPDATEUISTATE or WM_CHANGUISTATE in this example
#include <windows.h>
#include <tchar.h>
HINSTANCE g_hInst;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hBtn1, hBtn2;
switch(msg)
{
case WM_CREATE:
hBtn1 = CreateWindowEx(0, TEXT("Button"), TEXT("Button 1"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
4, 4, 100, 40, hwnd, 0, g_hInst, 0);
if(!hBtn1) return -1;
hBtn2 = CreateWindowEx(0, TEXT("Button"), TEXT("Button 2"), WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
114, 4, 100, 40, hwnd, 0, g_hInst, 0);
if(!hBtn1) return -1;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
const TCHAR szClassName[] = TEXT("Main////");
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);;
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if(!RegisterClassEx(&wc)) return 0;
g_hInst = hInstance;
hwnd = CreateWindowEx(0, szClassName, TEXT("Main"), WS_OVERLAPPEDWINDOW, 140, 140, 440, 240, 0, 0, hInstance, 0);
if(!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
while(GetMessage(&msg, 0, 0, 0) > 0)
{
if(!IsDialogMessage(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
I've used spy++ for the main window and the 2 buttons when pressing the Tab key and I don't see neither WM_UPDATEUISTATE nor WM_CHANGEUISTATE anywhere, but the standard button control draws the focus rect. How does the standard button know when to draw the focus rect?
A window should show its focus indicator when it has focus. For that, a control should watch for WM_SETFOCUS and WM_KILLFOCUS. When it has the focus, a well-behaved control will check the UI state by sending WM_QUERYUISTATE to itself, and, if the UI state has the UISF_HIDEFOCUS flag set, the control would not draw the focus indicator.
The WM_CHANGEUISTATE and WM_UPDATEUISTATE are responsible for controlling whether a tree of windows (including controls) should display keyboard "cues" and focus indicators in general. For example, if the cues aren't showing and you tap the ALT key, these messages will update the UI state of the windows in the tree so that they start showing the cues. These messages are not used for hiding the focus indicator from one control and showing it on the next as the user TABs through the controls. That effect happens because the controls pay attention to the focus change messages.
Generally speaking, a custom control should watch for the focus change messages and let DefWindowProc handle the UI state messages.
Raymond Chen has a series of blog posts about the UI state and how these messages propagate.
Untangling the confusingly-named WM_UPDATEUISTATE and WM_CHANGEUISTATE messages
Who sends the initial WM_UPDATEUISTATE message?
Why does tapping the Alt key cause my owner-draw static control to repaint?
What is the documentation for SetParent trying to tell me about synchronizing the UI state?
Demonstrating what happens when a parent and child window have different UI states
Getting a parent and child window to have the same UI states
I found these helpful when I recently tried to work all this out again. The current state of the official documentation on this topic is a bit vague and ambiguous in places.
Note: The terms "mnemonics," "accelerators," and "hot keys" have blurred together, even in the official Microsoft documentation and some of the API names. When I say "keyboard cues," I'm mostly referring to mnemonics (sometimes called "shortcuts" or "access keys"). Those are the underlined characters in control labels and menu items that let the user know that ALT+<character> will select the labeled item.

Is it possible to eliminate flickering entirely when resizing a window?

Normally, even when using double buffering, when resizing a window, it seems that it's inevitable that the flickering will happen.
Step 1, the original window.
Step 2, the window is resized, but the extra area hasn't been painted.
Step 3, the window is resized, and the extra area has been painted.
Is it possible somehow to hide setp 2? Can I suspend the resizing process until the painting action is done?
Here's an example:
#include <Windows.h>
#include <windowsx.h>
#include <Uxtheme.h>
#pragma comment(lib, "Uxtheme.lib")
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL MainWindow_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);
void MainWindow_OnDestroy(HWND hWnd);
void MainWindow_OnSize(HWND hWnd, UINT state, int cx, int cy);
void MainWindow_OnPaint(HWND hWnd);
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex = { 0 };
HWND hWnd;
MSG msg;
BOOL ret;
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = WindowProc;
wcex.hInstance = hInstance;
wcex.hIcon = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
wcex.hCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
wcex.lpszClassName = TEXT("MainWindow");
wcex.hIconSm = wcex.hIcon;
if (!RegisterClassEx(&wcex))
{
return 1;
}
hWnd = CreateWindow(wcex.lpszClassName, TEXT("CWin32"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);
if (!hWnd)
{
return 1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (ret == -1)
{
return 1;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(hWnd, WM_CREATE, MainWindow_OnCreate);
HANDLE_MSG(hWnd, WM_DESTROY, MainWindow_OnDestroy);
HANDLE_MSG(hWnd, WM_SIZE, MainWindow_OnSize);
HANDLE_MSG(hWnd, WM_PAINT, MainWindow_OnPaint);
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
BOOL MainWindow_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
{
BufferedPaintInit();
return TRUE;
}
void MainWindow_OnDestroy(HWND hWnd)
{
BufferedPaintUnInit();
PostQuitMessage(0);
}
void MainWindow_OnSize(HWND hWnd, UINT state, int cx, int cy)
{
InvalidateRect(hWnd, NULL, FALSE);
}
void MainWindow_OnPaint(HWND hWnd)
{
PAINTSTRUCT ps;
HPAINTBUFFER hpb;
HDC hdc;
BeginPaint(hWnd, &ps);
hpb = BeginBufferedPaint(ps.hdc, &ps.rcPaint, BPBF_COMPATIBLEBITMAP, NULL, &hdc);
FillRect(hdc, &ps.rcPaint, GetStockBrush(DKGRAY_BRUSH));
Sleep(320); // This simulates some slow drawing actions.
EndBufferedPaint(hpb, TRUE);
EndPaint(hWnd, &ps);
}
Is it possible to eliminate the flickering?
When the window is updated during a drag operation, then the OS has to show something in the extended window region. If you can't provide anything then it will show the background until you do. Since you didn't specify any background you get blackness. Surely you ought to be specifying a background brush? Simply adding the following to your code makes the behaviour more palatable:
wcex.hbrBackground = GetStockBrush(DKGRAY_BRUSH);
However, if you take as long as 320ms to respond to a WM_PAINT then you ruin the resize UI for the user. It becomes jerky and unresponsive. The system is designed around the assumption that you can paint the window quickly enough for dragging to feel smooth. The right way to fix your problem is to make WM_PAINT run in a reasonable time.
If you really can't achieve quick enough painting for smooth dragging then I suggest a couple of alternatives:
Disable window updates during dragging. I'm sure this can be done for individual windows, but I can't remember how to do it off the top of my head.
Paint something fake whilst a resize/drag is active, and postpone the real painting until when the resize/drag has completed. Listening for WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE are the keys to this. This Microsoft sample program illustrates how to do that: https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/winui/fulldrag/
Use WM_SIZING instead of WM_SIZE and don't forget about WM_ERASEBKGND.
If you go into the System Properties control panel applet, choose the Advanced tab, and then click Settings... in the Performance group box, you'll see a checkbox setting called Show window contents while dragging. If you uncheck that and try resizing a window, you'll see that only the window frame moves until you complete the drag operation, and then the window repaints just once at the new size. This is how window sizing used to work when we had slow, crufty computers.
Now we don't really want to change the setting globally (which you would do by calling SystemParametersInfo with SPI_SETDRAGFULLWINDOWS, but don't really do that because your users won't like it).
What happens when the user grabs the resize border is that the thread enters a modal loop controlled by the window manager. Your window will get WM_ENTERSIZEMOVE as that loop begins and WM_EXITSIZEMOVE when the operation is complete. At some point you'll also get a WM_GETMINMAXINFO, which probably isn't relevant to what you need to do. You'll also get WM_SIZING, WM_SIZE messages rapidly as the user drags the sizing frame (and the rapid WM_SIZEs often lead to WM_PAINTs).
The global Show window contents while dragging setting is responsible for getting the rapid WM_SIZE messages. If that setting is off, you'll just get one WM_SIZE message when it's all over.
If your window is complicated, you probably have layout code computing stuff (and maybe moving child windows) in the WM_SIZE handler and a lot of painting code in the WM_PAINT handler. If all that code is too slow (as your sample 320 ms delay suggests), then you'll have a flickery, jerky experience.
We really don't want to change the global setting, but it does inspire a solution to your problem:
Do simpler drawing during the resize operation and then do your (slower) complex drawing just once when the operation is over.
Solution:
Set a flag when you see the WM_ENTERSIZEMOVE.
Change your WM_SIZE handler to check the flag and do nothing if it's set.
Change your WM_PAINT handler to check the flag and do a simple, fast fill of the window in a solid color if it's set.
Clear the flag when you see WM_EXITSIZEMOVE, and then trigger your layout code and invalidate your window so that everything gets updated based on the final size.
If your slow window is a child rather than your application's top-level window, you'll have to signal the child window when the top-level window gets the WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE in order to implement steps 1 and 4.
Yes you can entirely delete flickering :)
You can do all the window message handling in one thread, and painting its context in another. Your window always keeps responsive. It works great, can not understand why this is not established best practice.
If you bind a Direct3D context for example, it can have an instant scaling while resizing, completely without having the context updated!
My code looks like this:
int WINAPI wWinMain( HINSTANCE a_hInstance, HINSTANCE a_hPrevInstance, LPWSTR a_lpCmdLine, int a_nCmdShow )
{
Win32WindowRunnable* runnableWindow=new Win32WindowRunnable(a_hInstance, a_nCmdShow);
IThread* threadWindow=new Win32Thread(runnableWindow);
threadWindow->start();
Scene1* scene=new Scene1(runnableWindow->waitForWindowHandle());
IThread* threadRender=new Win32Thread(scene);
threadRender->start();
threadWindow->join();
threadRender->pause();
threadRender->kill();
delete runnableWindow;
return 0;
}
Full source example here:
https://github.com/TheWhiteAmbit/TheWhiteAmbit/blob/master/Win32App/Win32Main.cpp

Where do I add my actual program after I establish a Window using WinMain() and WindowProc()? (C++)

First of all, sorry for my ignorance of the window creation process. Today is actually the first day of my experimenting with it.
I started to code a text based game a few days ago and I have the main menu, and 3 or 4 different functions that control various things with text. I was then advised to look into Windows API and create a window for the program. I have created the window which can be seen here:
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PWSTR nCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"WindowClass";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = CLASS_NAME;
wc.hInstance = hInstance;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx( //This creats a new instance of a window
0,
CLASS_NAME,
L"MyFirstWindow",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
500,
NULL,
NULL,
hInstance,
NULL);
if(hwnd == 0)
return 0;
ShowWindow(hwnd,nCmdShow);
nCmdShow = 1;
MSG msg = { };
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_DESTROY:PostQuitMessage(0); return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd,&ps);
FillRect(hdc,&ps.rcPaint,(HBRUSH)(COLOR_WINDOW+5));
EndPaint(hwnd, &ps);
}return 0;
case WM_CLOSE:
{
if(MessageBox(hwnd,L"Do you want to exit?",L"Exit",MB_OKCANCEL)==IDOK)
DestroyWindow(hwnd);
} return 0;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
This looks a bit messy, but you probably will not need it anyway.
So at this point I have my original program and this program that creates a window. My question is how, or even where, do I put my original program's code so that it can be incorporated into the window.
If you are reading this and thinking I'm a total moron for doing it this way, I'm open to ideas that are a lot simpler than what I'm doing right now.
Your code is the standard boilerplate for creating a window using c and win32 API functions. I recommend that you modify the message pump (it's the while loop in middle calling GetMessage). Instead do this:
Run an infinite loop
Peek a message
If the message is not there, execute your code
Else process messages including the quit message
Here's what the code should look like:
while (1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
//Your game code
}
}
I also want to point, that while learning game programming using C and calling Win32 API is a worthy goal, you might want to look at XNA game studio.
The reason I am recommending it is because it is easier to learn and you can make much more interesting games faster. Here are a few links to get you started if you are interested.
http://joshua-w-wise78.livejournal.com/
http://www.krissteele.net/blogdetails.aspx?id=72
http://www.codeproject.com/KB/game/xna1.aspx
http://msdn.microsoft.com/en-us/library/bb203894.aspx
If your original program was a console app, that read input and printed output, then you will probably want to get input from your window to implement your game.
Instead of looking at it from the perspective of read user input from stdin then generate output to stdout, you have to think of it from the view of window messaging. So you need to process the WM_KEYDOWN messages, you can then use DrawText() to show the user input in your client area, or you could use a c++ RichEdit control. Once you process the WM_KEYDOWN messages you know what the user has pressed and then your program can do it's thing (maybe being triggered to process an accumalated buffer of characters whenever the WM_KEYDOWN key is equal to the enter key?) and write the output to your client area using DrawText() or send WM_KEYDOWN messages to your richedit window using SendMessage().
I think this is what you meant by a text based game, anyway, you just have to start thinking of doing everything by monitoring windows messages. Anything that the user does to your window, Windows will send a message to it to let it know.

Resources