4
votes

I'm currently getting back to some Windows Programming using Petzold's book (5th edition). I compiled the following example using BitBlt and it doesn't work as it is supposed to.

It should copy the Window's icon of (CxSource, CySource) size and replicate it on the whole window's surface. What happens, in reality, using Windows 7 is that the bitmap below the window gets sourced and copied into the drawing surface i.e. hdcClient.

I don't understand why it behaves like this knowing that it's clear the DC passed to BitBlt is hdcWindow, which refers to a device context obtained via a GetWindowDC(hwnd) of the current application.

I first thought it was due to the fact the transparency mode is enabled by default, but deactivating it doesn't change anything. BitBlt seems to always take the surface below the application Window! I don't get it! :) Anyone knows why it works that way and how to fix it?

2
GetWindowDC should still work as it is a part of the Win32 API (user32.lib).zx485
Please don't post external link. Insert the code in your question.Barmak Shemirani
Hmmm ... e̶a̶t̶i̶n̶g̶ deleting my previous comments. Maybe I need to see that result. It sounds as if cxSource and cySource point to the wrong part of the window. It still is possible that "frame thickness + default size" is no longer a reliable way to grab the window icon, but that, or an alternative, seems not mentioned on MSDN.Jongware
@BarmakShemirani: I would agree with you in general about non-linking, but in this particular case mentioning github page specifically created for Petzold book with code sample looks perfectly ok to me.mvp
@BarmakShemirani For me it's not absolutely clear if publishing source code from Petzold's books on SO can be considered "fair use" as he writes it in his FAQ. So better safe than sorry.zett42

2 Answers

7
votes

Making screenshots with BitBlt() did not exactly get any easier since the addition of the DWM (Desktop Window Manager, aka Aero). Petzold's sample code suffers from a subtle timing issue, it is making the screenshot too soon. It does so while Aero is still busy animating the frame, fading it into view. So you see what is behind the window, possibly already partly faded depending on how quickly the first WM_PAINT message is generated.

You can easily fix it by disabling the effect:

#include <windows.h>
#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")

And after the CreateWindow() call:

BOOL disabled = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disabled, sizeof(disabled));

Another tricky detail is that the first BitBlt matters, the DWM returns a cached copy afterwards that is not correctly invalidated by the animation.

This gets grittier when you need a screenshot of a window that belongs to another process. But that was already an issue before Aero, you had to wait long enough to ensure that the window was fully painted. Notable perhaps is the perf of BitBlt(), it gets bogged-down noticeably by having to do job of composing the final image from the window back-buffers. Lots of questions about that at SO, without happy answers.

4
votes

It is not supposed to copy the windows icon, it is supposed to copy the windows titlebar part where the icon is located.

There are some issues with this (now 20 year old code):

  • GetSystemMetrics values cannot be used for window related dimensions anymore since GetSystemMetrics returns the classic sizes, not the Visual Style sizes.

  • Depending on the Windows version, the DWM might define the window size as something larger than your window (where it draws the window shadow and other effects).

Your example works OK on XP:

XP

(There is a small problem because the titlebar is not square (unlike Windows 98/2000 that this example was designed for) so you see a issue in the top left where it is just white. I also modified the example slightly so it varies the HDC source location)

On a modern version of Windows it seems like the DWM or something is not able to properly emulate a simple window DC and parts of the shadow/border/effects area is part of the DC:

Windows 8

I don't know how to fix this but the example is pretty useless anyway, if you want to draw the window icon you should draw the HICON with DrawIconEx. If you want to draw custom non-client area stuff then you need to find more recent examples, not something that only supports the classic theme.