Search code examples
mfcscreenshotaerodwmbitblt

How to correctly screencapture a specific window on Aero/DWM


Background info: I have this MFC application I coded and been using for a long time that pretty much automatically saves screenshots to the hard disk when the user hits the Print Screen/Alt+Print Screen key. I have been putting off using anything related to Aero until now that I've been using Windows 7 RC for a couple of weeks.

The problem: I'm using the standard GetDC/BitBlt method to capture the window contents. I have no problems with this method while doing regular full-screen grabs (no matter how many windows are opened etc). The problem arises when I try capturing the foreground window (Alt+PrintScreen). Here are two examples:

Example 1 http://indiecodelabs.com/extern/example1.jpg

Example 2 http://indiecodelabs.com/extern/example2.jpg

As you can see, I'm getting garbage where the borders should be. This is more noticeable towards the top, where we can see some duplication of the toolbar in both screenshots.

I've been googling about this for hours now and all I can find are articles saying that under DWM the BitBtl/GetDC method won't work, but can't find a single one explaining what we (the developers) should do to be able to maintain the same functionality in our apps when running on DWM.

Any help, pointers, suggestions will be greatly appreciated.


Solution

  • It's an excellent question that I unfortuneatly don't know exact answer to. My first idea was to grab the whole desktop and cut interesting part out of it.

    I've dug into QT 4.5 sources to see how they do it, and found something like this. If you switch GetClientRect to GetWindowRect and strip QT boilerplate code you should get what you want. It looks like a hack though :)

    
    QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h )
    {
        RECT r;
        GetClientRect(winId, &r);  
        if (w < 0) w = r.right - r.left;
        if (h < 0) h = r.bottom - r.top;  
        // Create and setup bitmap
        HDC display_dc = GetDC(0);
        HDC bitmap_dc = CreateCompatibleDC(display_dc);
        HBITMAP bitmap = CreateCompatibleBitmap(display_dc, w, h);
        HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
    
        // copy data
        HDC window_dc = GetDC(winId);
        BitBlt(bitmap_dc, 0, 0, w, h, window_dc, x, y, SRCCOPY);
    
        // clean up all but bitmap
        ReleaseDC(winId, window_dc);
        SelectObject(bitmap_dc, null_bitmap);
        DeleteDC(bitmap_dc);
    
        QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap);
    
        DeleteObject(bitmap);
        ReleaseDC(0, display_dc);
    
        return pixmap;
    }