BitBlt忽略CAPTUREBLT,似乎总是捕获目标的缓存副本 [英] BitBlt ignores CAPTUREBLT and seems to always capture a cached copy of the target

查看:120
本文介绍了BitBlt忽略CAPTUREBLT,似乎总是捕获目标的缓存副本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



如果我关闭然后重新打开窗口并截图,则非客户区域将被捕获。移动/调整窗口大小之后的任何后续捕获对捕获的屏幕截图均无效。再次,该客户区将是正确的。



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

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

开关(标志)
{
case WindowManagerUtils :: GrabWindowRect:
GetWindowRect(windowId,& r);
休息时间;
case WindowManagerUtils :: GrabClientRect:
GetClientRect(windowId,& r);
休息时间;
case WindowManagerUtils :: GrabScreenWindow:
GetWindowRect(windowId,& r);
返回QPixmap :: grabWindow(QApplication :: desktop()-> winId(),r.left,r.top,r.right-r.left,r.bottom-r.top);
case WindowManagerUtils :: GrabScreenClient:
GetClientRect(windowId,& r);
返回QPixmap :: grabWindow(QApplication :: desktop()-> winId(),r.left,r.top,r.right-r.left,r.bottom-r.top);
默认值:
返回QPixmap();
}

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

如果(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

//在开始创建对象之前,让我们确定以下内容,以免乱七八糟
Q_ASSERT(flags == WindowManagerUtils :: GrabWindowRect || flags == WindowManagerUtils :: GrabClientRect);

//创建和设置位图
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位图= CreateCompatibleBitmap(display_dc,w,h);
HGDIOBJ null_bitmap = SelectObject(bitmap_dc,位图);

//复制数据
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);

//清除除位图以外的所有内容
ReleaseDC(windowId,window_dc);
SelectObject(bitmap_dc,null_bitmap);
DeleteDC(bitmap_dc);

QPixmap pixmap = QPixmap :: fromWinHBITMAP(bitmap);

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

返回像素图;
}

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


grabWindow()函数从屏幕而不是从窗口$ b $抓取像素
。 b,例如,如果在您
抓取的窗口上有另一个窗口
部分或全部,您也会从
上方的窗口中获得像素。


但是,无论CAPTUREBLT标志如何,我都会遇到完全相反的情况。我已经尝试了所有我能想到的东西...没有任何效果。有想法吗?

解决方案

您对 BitBlt CAPTUREBLT 行为的困惑会来

它声明

CAPTUREBLT- -包含在结果图像中位于窗口顶部的所有窗口。默认情况下,图像仅包含您的窗口。



实际上是什么意思(至少对于未启用Aero的任何Windows操作系统)
CAPTUREBLT-包括与您的窗口重叠的任何分层(!)窗口(请参见WS_EX_LAYERED扩展窗口样式)。与您的窗口重叠的非分层窗口为



没有 WS_EX_LAYERED 扩展窗口样式且与您的窗口重叠的Windows不包含或不包含 CAPTUREBLT 标志(至少对于未启用Aero的任何Windows操作系统)。



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



ADD:



如果要捕获屏幕上的窗口,则必须使用CAPTUREBLT标志捕获整个桌面,然后使用窗口提取矩形。 (QT开发人员应该做同样的事情)。在两种情况下都可以正常运行:启用和不启用Aero均可。


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?

解决方案

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.

这篇关于BitBlt忽略CAPTUREBLT,似乎总是捕获目标的缓存副本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆