BitBlt ignores CAPTUREBLT and seems to always capture a cached copy of the target

こ雲淡風輕ζ 提交于 2019-11-30 09:12:14

问题


I am trying to capture screenshots using the BitBlt function. However, every single time I capture a screenshot, the non-client area NEVER changes no matter what I do. It's as if it's getting some cached copy of it. The client area is captured correctly.

If I close and then re-open the window, and take a screenshot, the non-client area will be captured as it is. Any subsequent captures after moving/resizing the window have no effect on the captured screenshot. Again, the client area will be correct.

Furthermore, the CAPTUREBLT flag seems to do absolutely nothing at all. I notice no change with or without it. Here is my capture code:

QPixmap WindowManagerUtils::grabWindow(WId windowId, GrabWindowFlags flags, int x, int y, int w, int h)
{
    RECT r;

    switch (flags)
    {
        case WindowManagerUtils::GrabWindowRect:
            GetWindowRect(windowId, &r);
            break;
        case WindowManagerUtils::GrabClientRect:
            GetClientRect(windowId, &r);
            break;
        case WindowManagerUtils::GrabScreenWindow:
            GetWindowRect(windowId, &r);
            return QPixmap::grabWindow(QApplication::desktop()->winId(), r.left, r.top, r.right - r.left, r.bottom - r.top);
        case WindowManagerUtils::GrabScreenClient:
            GetClientRect(windowId, &r);
            return QPixmap::grabWindow(QApplication::desktop()->winId(), r.left, r.top, r.right - r.left, r.bottom - r.top);
        default:
            return QPixmap();
    }

    if (w < 0)
    {
        w = r.right - r.left;
    }

    if (h < 0)
    {
        h = r.bottom - r.top;
    }

#ifdef Q_WS_WINCE_WM
    if (qt_wince_is_pocket_pc())
    {
        QWidget *widget = QWidget::find(winId);
        if (qobject_cast<QDesktopWidget*>(widget))
        {
            RECT rect = {0,0,0,0};
            AdjustWindowRectEx(&rect, WS_BORDER | WS_CAPTION, FALSE, 0);
            int magicNumber = qt_wince_is_high_dpi() ? 4 : 2;
            y += rect.top - magicNumber;
        }
    }
#endif

    // Before we start creating objects, let's make CERTAIN of the following so we don't have a mess
    Q_ASSERT(flags == WindowManagerUtils::GrabWindowRect || flags == WindowManagerUtils::GrabClientRect);

    // Create and setup bitmap
    HDC display_dc = NULL;
    if (flags == WindowManagerUtils::GrabWindowRect)
    {
        display_dc = GetWindowDC(NULL);
    }
    else if (flags == WindowManagerUtils::GrabClientRect)
    {
        display_dc = GetDC(NULL);
    }

    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 = NULL;
    if (flags == WindowManagerUtils::GrabWindowRect)
    {
        window_dc = GetWindowDC(windowId);
    }
    else if (flags == WindowManagerUtils::GrabClientRect)
    {
        window_dc = GetDC(windowId);
    }

    DWORD ropFlags = SRCCOPY;
#ifndef Q_WS_WINCE
    ropFlags = ropFlags | CAPTUREBLT;
#endif

    BitBlt(bitmap_dc, 0, 0, w, h, window_dc, x, y, ropFlags);

    // clean up all but bitmap
    ReleaseDC(windowId, window_dc);
    SelectObject(bitmap_dc, null_bitmap);
    DeleteDC(bitmap_dc);

    QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap);

    DeleteObject(bitmap);
    ReleaseDC(NULL, display_dc);

    return pixmap;
}

Most of this code comes from Qt's QWidget::grabWindow function, as I wanted to make some changes so it'd be more flexible. Qt's documentation states that:

The grabWindow() function grabs pixels from the screen, not from the window, i.e. if there is another window partially or entirely over the one you grab, you get pixels from the overlying window, too.

However, I experience the exact opposite... regardless of the CAPTUREBLT flag. I've tried everything I can think of... nothing works. Any ideas?


回答1:


Your confusion about BitBlt with CAPTUREBLT behaviour comes from the fact that official BitBlt documentation is unclear and misleading.

It states that
"CAPTUREBLT -- Includes any windows that are layered on top of your window in the resulting image. By default, the image only contains your window."

What actually means (at least for any windows OS without Aero enabled) "CAPTUREBLT -- Includes any layered(!) windows (see WS_EX_LAYERED extended window style) that overlap your window. Non-layered windows that overlap your window is never included."

Windows without WS_EX_LAYERED extended window style that overlap your window is not included with or without CAPTUREBLT flag (at least for any windows OS without Aero enabled).

QT developers also misunderstood BitBlt/CAPTUREBLT documentation so QT documentation is actually wrong about QPixmap::grabWindow behaviour on WIN32 platform without Aero enabled.

ADD:

If you want to capture your window as it is on the screen you have to capture the entire desktop with CAPTUREBLT flag and then extract the rectangle with your window. (QT developers should do the same thing). It will work correctly in both cases: with and without Aero enabled/available.




回答2:


I capture all screen and obtains the same results... :(

const uint SRCCOPY = 0x00CC0020; //SRCCOPY
    const uint CAPTUREBLT = 0x00CC0020 | 0x40000000; //CAPTUREBLT

    bool dv = BitBlt(hCaptureDC, 0, 0, Bounds.Width, Bounds.Height,
             hDesktopDC, Bounds.Left, Bounds.Top, _with_tooltips ? CAPTUREBLT : SRCCOPY);


来源:https://stackoverflow.com/questions/2899655/bitblt-ignores-captureblt-and-seems-to-always-capture-a-cached-copy-of-the-targe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!