8

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 フラグに関係なく、正反対のことが発生します。考えられることはすべて試しました...何も機能しません。何か案は?

4

2 に答える 2

7

BitBltCAPTUREBLTの動作についての混乱は、BitBltの公式ドキュメントが不明確で誤解を招くという事実に起因しています。

「 CAPTUREBLT
-- 結果の画像にウィンドウの上に重ねられたウィンドウが含まれます。デフォルトでは、画像にはウィンドウのみが含まれます。」

(少なくともAeroが有効になっていないWindows OSの場合)実際に意味する こと

ウィンドウにオーバーラップするWS_EX_LAYERED拡張ウィンドウ スタイルのないウィンドウは、 CAPTUREBLTフラグの有無にかかわらず含まれません(少なくとも、Aero が有効になっていない Windows OS の場合)。

また、QT 開発者は BitBlt/CAPTUREBLT のドキュメントを誤解していたため、Aero が有効になっていない WIN32 プラットフォームでの QPixmap::grabWindow の動作について QT のドキュメントは実際には間違っています。

追加:

ウィンドウをそのまま画面にキャプチャする場合は、CAPTUREBLT フラグを使用してデスクトップ全体をキャプチャし、ウィンドウで四角形を抽出する必要があります。(QT 開発者は同じことを行う必要があります)。どちらの場合でも正しく動作します: Aero が有効/利用可能の有無にかかわらず。

于 2011-01-10T12:51:17.330 に答える