【问题标题】:BitBlt ignores CAPTUREBLT and seems to always capture a cached copy of the targetBitBlt 忽略 CAPTUREBLT 并且似乎总是捕获目标的缓存副本
【发布时间】:2011-02-23 09:17:28
【问题描述】:

我正在尝试使用 BitBlt 功能截取屏幕截图。但是,每次我截取屏幕截图时,无论我做什么,非客户区都不会改变。就好像它正在获取它的一些缓存副本。客户区被正确捕获。

如果我关闭然后重新打开窗口并截屏,非客户区将被原样捕获。移动/调整窗口大小后的任何后续捕获都不会影响捕获的屏幕截图。同样,客户区是正确的。

此外,CAPTUREBLT 标志似乎完全没有任何作用。无论有没有它,我都没有注意到任何变化。这是我的捕获代码:

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;
}

大部分代码来自 Qt 的 QWidget::grabWindow 函数,因为我想进行一些更改以使其更加灵活。 Qt 的文档指出:

grabWindow() 函数抓取像素 从屏幕,而不是从窗口, 即如果有另一个窗口 部分或全部超过你 抓取,您从 覆盖窗口也是如此。

但是,我的体验完全相反……不管 CAPTUREBLT 标志如何。我已经尝试了我能想到的一切......没有任何效果。有什么想法吗?

【问题讨论】:

    标签: c++ windows qt screen-capture


    【解决方案1】:

    我捕获所有屏幕并获得相同的结果... :(

    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);
    

    【讨论】:

      【解决方案2】:

      您对 BitBltCAPTUREBLT 行为的混淆是因为官方 BitBlt 文档不明确且具有误导性。

      它声明
      “CAPTUREBLT -- 在生成的图像中包括在您的窗口顶部分层的任何窗口。默认情况下,该图像仅包含您的窗口。”

      实际上意味着什么(至少对于任何未启用 Aero 的 Windows 操作系统) “CAPTUREBLT -- 包括与您的窗口重叠的任何分层 (!) 窗口(请参阅 WS_EX_LAYERED 扩展窗口样式)。与您的窗口重叠的非分层窗口永远不会包括在内。”

      没有 WS_EX_LAYERED 扩展窗口样式的 Windows 与您的窗口重叠,无论是否带有 CAPTUREBLT 标志(至少对于任何未启用 Aero 的 Windows 操作系统)都不包括在内。

      QT 开发人员也误解了 BitBlt/CAPTUREBLT 文档,因此 QT 文档实际上是错误的关于 QPixmap::grabWindow 在未启用 Aero 的 WIN32 平台上的行为。

      添加:

      如果你想捕捉屏幕上的窗口,你必须用 CAPTUREBLT 标志捕捉整个桌面,然后用你的窗口提取矩形。 (QT 开发人员应该做同样的事情)。它在两种情况下都能正常工作:启用和不启用 Aero。

      【讨论】:

        猜你喜欢
        • 2016-11-27
        • 2012-08-24
        • 2012-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多