Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why this BitBlt example doesn't work anymore?

Tags:

c

windows

winapi

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?

like image 338
piquesel Avatar asked Dec 18 '22 02:12

piquesel


2 Answers

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.

like image 83
Hans Passant Avatar answered Dec 31 '22 20:12

Hans Passant


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.

like image 32
Anders Avatar answered Dec 31 '22 20:12

Anders