4

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?

piquesel
  • 157
  • 9
  • [GetWindowDC](https://msdn.microsoft.com/en-us/library/windows/desktop/dd144947(v=vs.85).aspx) should still work as it is a part of the Win32 API (user32.lib). – zx485 Jan 02 '18 at 22:32
  • 4
    Please don't post external link. Insert the code in your question. – Barmak Shemirani Jan 02 '18 at 22:35
  • 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 Jan 02 '18 at 22:47
  • 2
    @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 Jan 02 '18 at 22:59
  • @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](http://www.charlespetzold.com/faq.html). So better safe than sorry. – zett42 Jan 03 '18 at 10:08
  • These are stackoverflow's guidelines, not rules. You can ignore it if you wish. However the guidelines don't suggest that some websites or some authors are special. This is not a copyright issue, and it's not how copyright works. If an author doesn't want to be quoted anywhere on the internet then he can state that clearly. – Barmak Shemirani Jan 03 '18 at 11:07

2 Answers2

7

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.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    Thanks Hans. You are right on spot. Adding the DwmSetWindowAttribute allows the copy of the icon located in the titlebar. The code is in fact rather old, and it's even surprising that many of the examples I tried in the book remain compliant even with the latest Windows 10. – piquesel Jan 03 '18 at 15:43
4

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.

Anders
  • 97,548
  • 12
  • 110
  • 164
  • 1
    Nice work! But now I don't know how to call this one. OP is wrong in his assumption that BitBlt "doesn't work the way it is supposed to" – it still does. And the example code *does 100% what it should do* (copy a pretty random part of a window all over its client area). – Jongware Jan 02 '18 at 23:18
  • I did see some of the "bitmap below the window gets sourced" effect the OP talked about but it is very hard to diagnose what is going on. – Anders Jan 02 '18 at 23:19
  • Thanks for the comment Anders. I didn't express myself correctly, but in fact, it's supposed to copy the small icon in the title bar. As explained by Hans, disabling Aero on the specific Window does the trick and allows to obtain the expected behavior. My point was really to make sure I understand correctly how BitBlt works. Thanks for sharing all those thoughts. – piquesel Jan 03 '18 at 15:46
  • Yes, you can see if you look closely in my example that it is sort of transparent and part of some fast animation. DwmSetWindowAttribute does completely fix the issue. – Anders Jan 03 '18 at 19:50