Why should I call `GetDC` and `ReleaseDC` again and again? - windows

Every example code I have seen are GetDC and ReleaseDC being called again and again. Or BeginPaint and EndPaint.
But I think drawing on the screen happens very frequently (especially in a game), so storing the drawing in memory is better than getting and releasing device contexts all time.
So I went the route of getting a DC and "keeping it", only releasing it when the program ends. But why don't people do it like this? Is it because GetDC and ReleaseDC cost very little?
case WM_CREATE:
hdc = GetDC(hWnd); //hdc is declared as global HDC
MyBitmap = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1));
return 0;
case WM_PAINT:
MemDC = CreateCompatibleDC(hdc);
OldBitmap = (HBITMAP)SelectObject(MemDC, MyBitmap);
BitBlt(hdc, 0, 0, 300, 300, MemDC, 0, 0, SRCCOPY);
SelectObject(MemDC, OldBitmap);
DeleteDC(MemDC);
return 0;

The code you presented here is wrong. First off, you need to read a little more of the documentation. Here is a useful link: Painting and Drawing. Basically there are two ways to update a window:
In response to the WM_PAINT message. Use the BeginPaint and EndPaint functions to paint the client area properly. This message is sent by the system when a part of the client area is "invalidated" (as a result of resizing, restoring from minimized state, moving a window previously obscured, or programmatically invalidating it.) WM_PAINT is a low-priority message, received just before the message-queue gets empty.
Specifically drawing a part or the whole client area without having an invalidated region on it. Use GetDC and ReleaseDC for this. Useful if you want to make changes immediately visible, when the application (CPU) is busy.
Writing some code to process the WM_PAINT message is normally almost mandatory, while specifically drawing as well is optional, depending on the requirements of your application.
Never send or post a WM_PAINT message yourself, instead invalidate a part or the client area - the application will receive a WM_PAINT message before becoming idle. If you want the painting to occur immediately call UpdateWindow - this bypasses the message queue.

A recent (Aug 2016) security update in Windows 10 prevents the reuse of printer device contexts. After printing one document Windows 10 will refuse to print again with that same DC. It has always been a preferred practice to create a new DC for each document, but now it seems to be a requirement in Windows 10.

Related

Flicker when moving/resizing window

I have developed an application to display jpeg images. It can display 4 images, one in each quadrant of the screen. It uses 4 windows for that. The windows have no border (frame) nor titlebar.
When a new image is loaded, the window size is adjusted for the new image and then the image is displayed.
Especially when the window is made larger, there is often a flicker. With my eyes to slits, it seems that the old contents is moved when resizing before the new contents is displayed.
I consulted many resources and used all tricks:
the window has only style CS_DBLCLKS (no CS_HREDRAW or CS_VREDRAW);
the background brush is NULL;
WM_ERASEBKGND returns 1;
WM_NCPAINT returns 0;
WM_NCCALCSIZE tells to align to the side not moved (can you tell it to discard the client area?);
WM_WINDOWPOSCHANGING returns 0;
SetWindowPos has flags SWP_NOCOPYBITS | SWP_DEFERERASE | SWP_NOREDRAW | SWP_NOSENDCHANGING.
Still, the flicker (or the contents move) occurrs when resizing the window. What I want is to:
SetWindowPos to new size and position;
InvalidateRect (hWnd, NULL, FALSE);
UpdateWindow(hWnd);
without any painting, background erasing or content moving until WM_PAINT.
WM_PAINT does:
hDC= BeginPaint (hWnd, &ps);
hMemDC= CreateCompatibleDC (hDC);
hOldBitmap = SelectObject (hMemDC, hNewBitmap);
BitBlt (hDC,...,hMemDC,0,0,SRCCOPY);
SelectObject (hMemDC, hOldBitmap);
DeleteDC (hMemDC);
EndPaint (hWnd, &ps);
Can anyone tell me if/where I make a mistake that causes the old content of the window to be moved?
Hardware etc: Windows 7 on HP Elitebook Core7 64 bits with NVIDIA Quadro K1000m driver 9.18.13.3265 (updated to 341.44).
UPDATE (Jul '17)
I have seen the behavior of the pogram also on another Windows computer (Windows 8/10). It does not seem to be the NVIDIA display driver.
The behavior is the most visible when resizing a window tiled to the centre of the screen (right bottom = w/2, h/2) to the left or left upper corner (0, 0).
I may have problems with the calculations for the WM_NCCALCSIZE message to tell Windows not to do anything. Could anyone give an example calculation for my purpose? See also How do I force windows NOT to redraw anything in my dialog when the user is resizing my dialog?
You have an impressive list of anti-flickering tricks :-)
I don't know if this is of importance (since it depends on how your tool windows are created and espacially if they are child windows to a common parent):
Try setting window style WS_CLIPCHILDREN in the parent window of the tool windows (if there is one).
If not set the parent window will erase it's (entire) background and then forward the paint messages to the child windows which will cause flickering. If WS_CLIPCHILDREN is set the parent window does nothing to the client area occupied by child windows. As a result the area of child windows isn't drawn twice and there is no chance for flickering.
This is a theory more than an answer:
By default, in modern Windows, your window is just a texture on the video card, and the desktop window manager is mapping that to a rectangle on the screen. You've seem to have done everything necessary to make sure that texture gets updated in one fell swoop.
But when you resize the window, perhaps the desktop compositor immediately updates its geometry, causing the (still unchanged) texture to be appear in the new position on the screen. It's only later, when you do the paint, that the texture is updated.
You can test this theory by temporarily turning off desktop compositing. On Windows 7, you navigate to System Properties, choose the Advanced tab, under Performance choose Settings..., on the Visual Effects tab, deselect the "Enable desktop composition" setting. Then try to reproduce the problem. If it goes away, then that supports (but doesn't absolutely prove) my theory.
(Remember to re-enable compositing, since that's how most of your users will be running most of the time.)
If the theory is true, it seems the goal is to get to the paint as soon possible after the window resize. If the window of time is small enough, then both could happen within a monitor refresh cycle and there would be no flicker.
Ironically, your efforts to eliminate flicker may be working against you here, since you've intentionally suppressed the invalidation and redraw that would normally result from SetWindowPos until you do it manually at a later step.
A debugging tip: Try introducing delays (e.g., Sleep(1000);) at key points in the process so you can see whether the resize and redraw are actually rendering on screen as two distinct steps.
You have indeed got a nice set of tricks.
First I can suggest a few variants on the existing tricks that might help especially on XP/Vista/7, and second I want to mention where the persistent flicker you see on Win8/10 is likely coming from and some tricks to reduce that.
First, despite advice to the contrary in other OP posts, you may find that if you set the CS_HREDRAW|CS_VREDRAW window styles that you avoided before, it actually eliminates the BitBlt that is done inside the internal SetWindowPos Windows does during window resizing (but---and this is the confusing part---on Windows 8/10 you will still see flicker from another source...more on that below).
If you don't want to include CS_HREDRAW|CS_VREDRAW, you can also intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing.
Or, you could add to your WM_NCCALCSIZE trick by having it return a set of values that will tell Windows to just BitBlt 1 pixel on top of itself so that even if the BitBlt does happen, it doesn't do anything visibly:
case WM_NCCALCSIZE:
{
RECT ocr; // old client rect
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new window rect
// np->rgrc[1] is old window rect
// np->rgrc[2] is old client rect
ocr = np->rgrc[2];
}
else
{
RECT *r = (RECT *)lParam;
// *r is window rect
}
// first give Windoze a crack at it
lRet = DefWindowProc(hWnd, uMsg, wParam, lParam);
if (wParam)
{
NCCALCSIZE_PARAMS *np = (NCCALCSIZE_PARAMS *)lParam;
// np->rgrc[0] is new client rect computed
// np->rgrc[1] is going to be dst blit rect
// np->rgrc[2] is going to be src blit rect
//
ncr = np->rgrc[0];
RECT &dst = np->rgrc[1];
RECT &src = np->rgrc[2];
// FYI DefWindowProc gives us new client rect that
// - in y
// - shares bottom edge if user dragging top border
// - shares top edge if user dragging bottom border
// - in x
// - shares left edge if user dragging right border
// - shares right edge if user dragging left border
//
src = ocr;
dst = ncr;
// - so src and dst may have different size
// - ms dox are totally unclear about what this means
// https://learn.microsoft.com/en-us/windows/desktop/
// winmsg/wm-nccalcsize
// https://learn.microsoft.com/en-us/windows/desktop/
// api/winuser/ns-winuser-tagnccalcsize_params
// - they just say "src is clipped by dst"
// - they don't say how src and dst align for blt
// - resolve ambiguity
// essentially disable BitBlt by making src/dst same
// make it 1 px to avoid waste in case Windoze still does it
dst.right = dst.left + 1;
dst.bottom = dst.top + 1;
src.right = dst.left + 1;
src.bottom = dst.top + 1;
lRet = WVR_VALIDRECTS;
}
else // wParam == 0: Windoze wants us to map a single rect w->c
{
RECT *r = (RECT *)lParam;
// *r is client rect
}
return lRet;
So that's all very nice, but why does Windows 8/10 look so bad?
Apps under Windows 8/10 Aero don't draw directly to the screen, but rather draw to offscreen buffers that are then composited by the evil DWM.exe window manager. DWM actually adds another layer of BitBlt-type behavior on top of the existing legacy XP/Vista/7 BitBlt behavior.
And the DWM blit behavior is even more crazy because they don't just copy the client area, but they actually replicate pixels at the edges of your old client area to make the new one. Unfortunately, making DWM not do its blit is much harder than just passing some extra flags.
I don't have a 100% solution, but I hope the above info will help, and please see this Q&A for a timing trick you can use to reduce the chances of the DWM-layer blit happening to your app:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Just in addition to your list of tricks. I have the same problem with flicker on my Dell XPS notebook while resizing window using left/top edge. I've tried all the tricks you mentioned. As far as I understand window border is drawn in GPU and the window content is prepared in GDI subsystem and transferred to video memory for window composition (DWM introduced in Windows 8.1). I tried to remove GDI rendering completely (setting WS_EX_NOREDIRECTIONBITMAP style) which makes window without any content, and then create content surface directly using DXGI subsystem (using CreateSwapChainForComposition, there are few examples on how to do this). But the problem remains. There is still a lag between rendering a window frame and resizing/displaying content surface.
However it may solve your problem, as you don't have the border and you will not notice this lag. But you will have full control over window repaint and it will be made on GPU side.

how to refresh the screen using masm assembly or an equivalent to application::doevents() in masm assembly

when the program is doing calculations in c++,c#,vb.net you can use the application::doevents() to refresh the screen so the screen do not freeze. how can you do this in assembly particular masm
I tried putting
invoke SendMessage, hWin, WM_SETREDRAW, 0, 0
invoke SendMessage, hWin, WM_SETREDRAW, 1, 0
in the loop part of my program but it does not work.
I also tried
invoke SendMessage, hWin, WM_SETREDRAW, 1, 0
but that does not work either
Application::DoEvents does a lot more than just refresh the screen. It actually pumps the message queue for the application's UI thread. If you wanted to do this in assembler, you would need to pump the message queue yourself. However, I don't recommend this. Using DoEvents in any UI framework is a definite anti-pattern, whether it provides such a function or not. More information is here and here.
If all you want to do is ensure that your listbox control gets repainted, then you need to invalidate it and force a redraw. In WinForms, that would be:
myListBox.Invalidate();
myListBox.Update();
In assembler, you will need to invoke the appropriate Win32 API functions yourself. In particular, you can use InvalidateRect to invalidate the client area of your listbox control and then UpdateWindow to ensure that it gets repainted immediately.
I don't know the syntax for MASM, but in unmanaged C++, it would look like this:
InvalidateRect(hListBox, /* handle to your listbox control */
NULL, /* invalidate the entire client area */
TRUE); /* erase the background */
UpdateWindow(hListBox);
In most cases, invalidating the window is enough. You don't need to force an immediate repaint, because the next time that your application processes messages (in its regular message loop), it will process a WM_PAINT message and redraw the control. The only reason this wouldn't work is if you were blocking the UI thread by performing some type of long-running operation, which you should not be doing in the first place—spin off a background thread to perform calculations.
The reason sending a WM_SETREDRAW message doesn't work here is because that basically sets a flag indicating whether the control is allowed to redraw itself. As the linked documentation explains, the window still needs to be redrawn in order to see the changes. It says you can force this by calling, e.g., RedrawWindow or doing the same thing we did above.

Difference between GetDC() and BeginPaint()

I am working on Win32 UI. I want to know the difference Between GetDC and BeginPaint. When to Use which API and when not to use which API.
GetDC simply returns the handle to the device context, which can be used any time anywhere to do your own drawing. BeginPaint on the other hand prepares the window for painting, and also provides information on what should be painted (such as whether the background needs repainting and the rect that needs to be painted).
Examples of when to use each? BeginPaint is most commonly seen inside WM_PAINT handlers (MSDN: An application should not call BeginPaint except in response to a WM_PAINT message. Each call to BeginPaint must have a corresponding call to the EndPaint function.). GetDC can be used anywhere, so if you want to draw on an external window. Basically anytime thats not in a WM_PAINT handler. BeginPaint and EndPaint also have some affect on the caret. Read msdn for more details.
GetDC() is not a substitute for Begin+EndPaint(). If you try, you'll find that your UI thread starts to burn 100% cpu core and your WM_PAINT handler getting called over and over again.
The big one is BeginPaint(), it clears the update region of the window. The value of PAINTSTRUCT.rcPaint. WM_PAINT is generated as long as the window has a dirty rectangle, created by an InvalidateRect() call by either the window manager or your program explicitly calling it. BeginPaint() clears it.
BeginPaint is intended to be called only in response to WM_PAINT message. The device context obtained by it points to the invalidated (to-be-redrawn) area of the window. It should be then released using EndPaint.
GetDC can be called at any time. The device context obtained by it points to the whole client area of the window. To release it, you should call ReleaseDC.

What Windows message is sent to repaint a partially occluded window?

I know that WM_PAINT tells a window that it needs to repaint itself entirely, but apparently that's not the message that gets sent when it's been covered partially and then the window that was in front of it is no longer in the way and it needs to repaint the dirty portion. Does anyone know what message is sent in this case?
EDIT: Found the problem:
The issue involved a Delphi control I wrote to embed a SDL rendering surface on a Delphi form. SDL has an API to build its renderer on another window's HWND, and it marks it as a "foreign window".
SDL usually handles WM_PAINT internally, so I ended up having to add some code to SDL's WindowProc to forward the message on to the external WindowProc if it's a foreign window. That was working sometimes, but it turns out there was a glitch that was stripping the foreign window flag from the window's data structure, so it was swallowing the message instead of passing it on to my app. Once I fixed that, I started getting the WM_PAINT messages all the time.
Why do you say it's apparently not? WM_PAINT should be called for partial redraws (the updated rect is returned by BeginPaint or GetUpdateRect). If it doesn't appear to be getting called, there may be a bug elsewhere in your app that's preventing it. What are you seeing that leads you to believe that it's not working?
WM_PAINT is sent to indicate that some portion (including the entirity) of the window needs to be repainted.
Call GetUpdateRect() to get a rectangle that bounds the region to be updated. This information is also included in the PAINTSTRUCT (as the rcPaint field) passed to BeginPaint().
The BeginPaint() function returns the rect that requires validation in its 2nd parameter: http://msdn.microsoft.com/en-us/library/dd183362(VS.85).aspx
case WM_PAINT:
{
PAINTSTRUCT psPaint;
HDC hdc = BeginPaint( hwnd, &psPaint );
// psPaint.rcPaint contains invalidated area
EndPaint (hwnd, &psPaint);
}
return 0;
Look at psPaint.rcPaint : http://msdn.microsoft.com/en-us/library/dd162768(VS.85).aspx
I'm pretty certain the Win32 API uses WM_PAINT even for partial repaints. From MSDN:
The WM_PAINT message is sent when the system or another application makes a request to paint a portion of an application's window. [My italics].
That link has the full detail on WM_PAINT but if, as you say, the WM_PAINT message is not being sent for partial redraws, the Spy++ is the tool you need to find out for sure.
Take a look at WM_PRINTCLIENT. There are some circumstances when WM_PAINT is not sent and a WM_PRINTCLIENT message is sent instead. (AnimateWindow for example.)

How can I stop window rendering and later resume?

I'd like to prevent my window from being updated until I finish receiving data from the server and render it. Can I hook on the WM_PAINT event, or better still call some Win32API method to prevent the window from being updated and unfreeze it later?
More info:
In the context of an MMC snapin written in C#, our application suffers from annoying flickering and double sorting behaviour:
We use MMC's listViews, but since we subscribe to the sort event.
MMC does it's own magic and sorts the page being displayed (and we can't override that), and when we receive a reply from our server we change the listView again.
each row change is done sequentially, there's no beginUpdate etc. (AFAIK).
Normally hooking into WM_PAINT is the way to go, but make sure you also ignore all WM_ERASEBKGND notifcations, otherwise you'll still get flicker, because Windows erases the Windows area for you. (Return non-zero to prevent Windows from doing that)
One other possibility is to use the LockWindowUpdate function, but it has some drawbacks:
Only one window can be locked
Upon unlock the whole desktop and all sub-windows (i.e. everything) is repainted, resulting in short flash of the whole desktop. (It's worse on XP than on Vista)
Some controls have BeginUpdate and EndUpdate APIs for this purpose.
If you do something (e.g. hook and ignore paint events) do disable painting, then a way to force a repaint later is to call the Invalidate method.
OK, after all searching and checking I've found that LockUpdateWindow is bad idea - see for example articles of Raimond Chen OldNewThing. But even to implement the idea of SetRedrawWindow wasn't so simple - because what I had was only received from IConsole2* pConsole->GetMainWindow() HWND handler of main window. By setting it to SetRedraw = FALSE it was disappeared in very strange manner. Though to make the procedure run only for the TreeView and not for the whole application (ours left panel) I ran
EnumChildWindows(hWnd, SetChildRedraw, FALSE); //stopping redraw
//... here you do your operations
EnumChildWindows(hWnd, SetChildRedraw, TRUE); //restarting redraw
where SetChildRedraw callback was defined in next way:
#define DECLARE_STRING(str) TCHAR str[MAX_PATH]; ZeroMemory(str, sizeof(str));
BOOL CALLBACK SetChildRedraw(HWND hwndChild, LPARAM lParam)
{
RECT rcChildRect; ZeroMemory(&rcChildRect, sizeof(rcChildRect));
DECLARE_STRING(sText)
GetClassName(hwndChild, sText, MAX_PATH);
if (wcsstr(sText, L"SysTreeView32") != NULL)
{
SetWindowRedraw(hwndChild, lParam);
if (lParam == TRUE)
{
GetWindowRect(hwndChild, &rcChildRect);
InvalidateRect(hwndChild, &rcChildRect, TRUE);
}
}
return TRUE;
}

Resources