如何在 C++ 中使用 DirectComposition 和 Direct2D 创建屏幕截图不可见的叠加?

问题描述 投票:0回答:1

我正在尝试用 C++ 创建一个不会出现在屏幕截图中的叠加层。覆盖层应该对用户可见,但不会被屏幕截图捕获(这也将由代码完成)。我读到这可以通过 DirectComposition 和 Direct2D 实现,但我无法达到预期的效果。

当前行为:叠加层显示在屏幕上,但也会在屏幕截图中捕获。

期望的行为:我希望叠加层在屏幕上可见,但不在屏幕截图中捕获。

这是我正在使用的代码:

#include <windows.h>
#include <dcomp.h>
#include <d2d1.h>
#include <thread>
#include <iostream>
#include <fstream>

#pragma comment(lib, "dcomp.lib")
#pragma comment(lib, "d2d1.lib")

void OverlayThreadFunction() {
    const wchar_t CLASS_NAME[] = L"DirectCompWindowClass";

    WNDCLASSW wc = {};
    wc.lpfnWndProc = DefWindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND);
    RegisterClassW(&wc);

    HWND hwnd = CreateWindowExW(
            WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT,
            CLASS_NAME,
            L"DirectComposition Overlay",
            WS_POPUP | WS_VISIBLE,
            0, 0, 100, 100,
//            0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), (for fullscreen)
            nullptr, nullptr, GetModuleHandle(NULL), nullptr
    );

    if (!hwnd) {
        return;
    }

    // Set window transparency
    SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);

    // Initialize DirectComposition
    IDCompositionDevice *pDCompDevice = nullptr;
    HRESULT hr = DCompositionCreateDevice(nullptr, __uuidof(IDCompositionDevice),
                                          reinterpret_cast<void **>(&pDCompDevice));
    if (FAILED(hr)) {
        return;
    }

    IDCompositionTarget *pDCompTarget = nullptr;
    hr = pDCompDevice->CreateTargetForHwnd(hwnd, TRUE, &pDCompTarget);
    if (FAILED(hr)) {
        return;
    }

    IDCompositionVisual *pDCompVisual = nullptr;
    hr = pDCompDevice->CreateVisual(&pDCompVisual);
    if (FAILED(hr)) {
        return;
    }

    // Initialize Direct2D
    ID2D1Factory *pD2DFactory = nullptr;
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
    if (FAILED(hr)) {
        return;
    }

    ID2D1HwndRenderTarget *pRT = nullptr;
    D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties();
    D2D1_HWND_RENDER_TARGET_PROPERTIES hwndProps = D2D1::HwndRenderTargetProperties(hwnd, D2D1::SizeU(
            GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)));
    hr = pD2DFactory->CreateHwndRenderTarget(props, hwndProps, &pRT);
    if (FAILED(hr)) {
        return;
    }

    ID2D1SolidColorBrush *pBrush = nullptr;
    hr = pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red, 1.0f), &pBrush);
    if (FAILED(hr)) {
        return;
    }

    pDCompVisual->SetContent(pRT);

    // Set the visual as the root of the composition target
    hr = pDCompTarget->SetRoot(pDCompVisual);
    if (FAILED(hr)) {
        return;
    }

    // Commit the composition
    hr = pDCompDevice->Commit();
    if (FAILED(hr)) {
        return;
    }

    // Draw overlay
    pRT->BeginDraw();
    pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); // Fully transparent background
    pRT->FillRectangle(D2D1::RectF(100, 100, 400, 300), pBrush); // Solid red rectangle
    pRT->EndDraw();

    MSG msg = {};
    while (GetMessage(&msg, nullptr, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Clean up
    pBrush->Release();
    pRT->Release();
    pD2DFactory->Release();
    pDCompVisual->Release();
    pDCompTarget->Release();
    pDCompDevice->Release();
}

bool SaveBitmap(HBITMAP hBitmap, const char *filename) {
    BITMAP bmp;
    GetObject(hBitmap, sizeof(BITMAP), &bmp);

    LONG height = (bmp.bmHeight > 0) ? -bmp.bmHeight : bmp.bmHeight;

    BITMAPFILEHEADER bmfHeader;
    BITMAPINFOHEADER bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bmp.bmWidth;
    bi.biHeight = height;
    bi.biPlanes = 1;
    bi.biBitCount = 32; // Change this to 24 if your bitmap doesn't have alpha channel
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((bmp.bmWidth * bi.biBitCount + 31) / 32) * 4 * abs(height);

    // Add the size of the headers to the size of the bitmap to get the total file size
    DWORD dwSizeofDIB = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmpSize;

    // Offset to where the actual bitmap bits start.
    bmfHeader.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + (DWORD) sizeof(BITMAPINFOHEADER);

    // Size of the file
    bmfHeader.bfSize = dwSizeofDIB;

    // bfType must always be BM for Bitmaps
    bmfHeader.bfType = 0x4D42; //BM

    // Create the file
    std::ofstream file(filename, std::ios::out | std::ios::binary);
    if (!file) {
        std::cerr << "Error: Unable to create file: " << filename << std::endl;
        return false;
    }

    // Write the Bitmap file header
    file.write(reinterpret_cast<const char *>(&bmfHeader), sizeof(BITMAPFILEHEADER));

    // Write the Bitmap info header
    file.write(reinterpret_cast<const char *>(&bi), sizeof(BITMAPINFOHEADER));

    // Get the bitmap bits
    BYTE *pBits = new BYTE[dwBmpSize];
    GetBitmapBits(hBitmap, dwBmpSize, pBits);

    // Write the Bitmap bits
    file.write(reinterpret_cast<const char *>(pBits), dwBmpSize);

    // Clean up
    delete[] pBits;
    file.close();

    return true;
}

int main() {
    std::thread overlayThread(OverlayThreadFunction);

    Sleep(2000); //Sleep for 2secs. to ensure that overlay is rendered

    // Make screenshot
    HWND hDesktopWnd = GetDesktopWindow();
    HDC hDesktopDC = GetDC(hDesktopWnd);
    HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
    HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDesktopDC, GetSystemMetrics(SM_CXSCREEN),
                                                    GetSystemMetrics(SM_CYSCREEN));
    SelectObject(hCaptureDC, hCaptureBitmap);
    BitBlt(hCaptureDC, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), hDesktopDC, 0, 0, SRCCOPY);
    SaveBitmap(hCaptureBitmap, "desktop_screenshot.bmp");
    DeleteObject(hCaptureBitmap);
    DeleteDC(hCaptureDC);
    ReleaseDC(hDesktopWnd, hDesktopDC);

    std::cout << "Screenshot captured and saved\n";

    overlayThread.join();
    return 0;
}

我所说的“屏幕截图”并不一定是指 Windows 提供的标准屏幕截图功能。我也愿意接受涉及自定义屏幕截图代码的解决方案。如果有办法通过自编程的截图方法来实现这种隐形,那也是可以接受的。

任何有关如何使覆盖层在屏幕截图中不可见的建议将不胜感激!

c++ windows screenshot direct2d
1个回答
0
投票

您可以将

SetWindowDisplayAffinity()
WDA_EXCLUDEFROMCAPTURE
标志一起使用:

该窗口仅显示在监视器上。在其他地方,根本不会出现该窗口。

这种亲和力的一个用途是用于显示视频录制控件的窗口,以便这些控件不包含在捕获中。

在 Windows 10 Version 2004 中引入。请参阅有关以前版本的 Windows 的兼容性的备注。

© www.soinside.com 2019 - 2024. All rights reserved.