bluetooth16
bluetooth16

Reputation: 11

Capturing a window on Win 10 without WinRT

I am trying to capture a window (not a part of the screen) using swap chains on a Windows 10 machine without WinRT.

I ran a project with WinRT and it worked, suggesting my graphics card is capable of capturing windows.

However, I'm not getting any errors, but pStagingTexture is blank and imageData is full of zeroes. I tested SaveTextureAsPng() with the Windows Duplication API, capturing a screen rather than a window, and it worked, so SaveTextureAsPng() is likely not the issue.

I also tried capturing windows that are visible and minimized, but no luck. I have tried different surface flags. I know I can use GDI+ (I think), but I want to use the GPU (like a Quadro P4000, driver 472.98). I would also prefer that it works with C++14.

bool CaptureWindow(HWND hwnd, LPCWSTR filepath)
{
    HRESULT hr = S_OK;
    ID3D11Device* pDevice = nullptr;
    IDXGIDevice* pDXGIDevice = nullptr;
    IDXGIAdapter* pDXGIAdapter = nullptr;
    IDXGIOutput* pOutput = nullptr;
    IDXGIOutput1* pOutput1 = nullptr;
    IDXGIOutputDuplication* pOutputDuplication = nullptr;
    IDXGIFactory2* pFactory = nullptr;
    ID3D11Texture2D* pStagingTexture = nullptr;
    ID3D11DeviceContext* pImmediateContext = nullptr;

    RECT rect;
    UINT width, height;

    if (GetWindowRect(hwnd, &rect))
    {
        width = rect.right - rect.left;
        height = rect.bottom - rect.top;
    }
    else
    {
        std::cout << "hwnd doesn't have any dimensions" << std::endl;
        return false;
    }

    hr = D3D11CreateDevice(
        nullptr,
        D3D_DRIVER_TYPE_HARDWARE,
        nullptr,
        0,
        nullptr,
        0,
        D3D11_SDK_VERSION,
        &pDevice,
        nullptr,
        &pImmediateContext);

    if (FAILED(hr))
    {
        std::cout << "Failed to create D3D11 device." << std::endl;
        return false;
    }

    hr = pDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&pDXGIDevice));
    if (FAILED(hr))
    {
        std::cout << "Failed to retrieve DXGI device." << std::endl;
        pDevice->Release();
        return false;
    }

    hr = pDXGIDevice->GetAdapter(&pDXGIAdapter);
    if (FAILED(hr))
    {
        std::cout << "Failed to retrieve DXGI adapter." << std::endl;
        pDXGIDevice->Release();
        pDevice->Release();
        return false;
    }

    hr = pDXGIAdapter->EnumOutputs(0, &pOutput);
    if (FAILED(hr))
    {
        std::cout << "Failed to enumerate DXGI output." << std::endl;
        pDXGIAdapter->Release();
        pDXGIDevice->Release();
        pDevice->Release();
        return false;
    }

    // Get the DXGI factory
    hr = pDXGIAdapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&pFactory));
    if (FAILED(hr))
    {
        std::cout << "Failed to retrieve DXGI factory." << std::endl;
        pOutput->Release();
        pDXGIAdapter->Release();
        pDXGIDevice->Release();
        pDevice->Release();
        return false;
    }

    // Create swap chain description
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
    swapChainDesc.Width = width;
    swapChainDesc.Height = height;
    swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    swapChainDesc.Stereo = false;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 1;
    swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
    swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
    swapChainDesc.Flags = 0;

    // Create swap chain
    IDXGISwapChain1* pSwapChain = nullptr;
    hr = pFactory->CreateSwapChainForHwnd(
        pDevice,
        hwnd,
        &swapChainDesc,
        nullptr,
        nullptr,
        &pSwapChain);
    if (FAILED(hr))
    {
        std::cout << "Failed to create swap chain." << std::endl;
        pFactory->Release();
        pOutput->Release();
        pDXGIAdapter->Release();
        pDXGIDevice->Release();
        pDevice->Release();
        return false;
    }

    // Get the back buffer
    ID3D11Texture2D* pBackBuffer = nullptr;
    hr = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pBackBuffer));
    if (FAILED(hr))
    {
        std::cout << "Failed to get back buffer from swap chain." << std::endl;
        pSwapChain->Release();
        pFactory->Release();
        pOutput->Release();
        pDXGIAdapter->Release();
        pDXGIDevice->Release();
        pDevice->Release();
        return false;
    }

    ID3D11RenderTargetView* pRenderTargetView = nullptr;
    hr = pDevice->CreateRenderTargetView(pBackBuffer, nullptr, &pRenderTargetView);
    if (FAILED(hr))
    {
        std::cout << "Failed to create render target view" << std::endl;
        pBackBuffer->Release();
        pSwapChain->Release();
        pFactory->Release();
        pOutput->Release();
        pDXGIAdapter->Release();
        pDXGIDevice->Release();
        pDevice->Release();
        return false;
    }

    // Unbind the back buffer and staging texture
    pImmediateContext->OMSetRenderTargets(1, &pRenderTargetView, nullptr);
    pRenderTargetView->Release();

    // Create a staging texture
    D3D11_TEXTURE2D_DESC textureDesc = {};
    textureDesc.Width = width;
    textureDesc.Height = height;
    textureDesc.MipLevels = 1;
    textureDesc.ArraySize = 1;
    textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    textureDesc.SampleDesc.Count = 1;
    textureDesc.Usage = D3D11_USAGE_STAGING;
    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    textureDesc.MiscFlags = 0;
    textureDesc.BindFlags = 0; //D3D11_BIND_RENDER_TARGET; // | D3D11_BIND_SHADER_RESOURCE

    hr = pDevice->CreateTexture2D(&textureDesc, nullptr, &pStagingTexture);
    if (FAILED(hr))
    {
        std::cout << "Failed to create staging texture." << std::endl;
        pBackBuffer->Release();
        pSwapChain->Release();
        pFactory->Release();
        pOutput->Release();
        pDXGIAdapter->Release();
        pDXGIDevice->Release();
        pDevice->Release();
        return false;
    }

    // Copy the back buffer to the staging texture
    pImmediateContext->CopyResource(pStagingTexture, pBackBuffer);

    pImmediateContext->Flush();

    // Map the staging texture
    D3D11_MAPPED_SUBRESOURCE mappedResource = {};
    hr = pImmediateContext->Map(pStagingTexture, 0, D3D11_MAP_READ, 0, &mappedResource);
    if (FAILED(hr))
    {
        std::cout << "Failed to map staging texture." << std::endl;
        pStagingTexture->Release();
        pBackBuffer->Release();
        pSwapChain->Release();
        pFactory->Release();
        pOutput->Release();
        pDXGIAdapter->Release();
        pDXGIDevice->Release();
        pDevice->Release();
        return false;
    }

    // Save the mapped resource as an image
    //SaveMappedResourceAsPng(mappedResource, filename);
   

    // Calculate image size
    const UINT rowPitch = mappedResource.RowPitch;
    const UINT imageSize = rowPitch * textureDesc.Height;

    // Copy image data
    std::vector<BYTE> imageData(imageSize);
    memcpy(imageData.data(), mappedResource.pData, imageSize);

    // Unmap the staging texture
    pImmediateContext->Unmap(pStagingTexture, 0);

    SaveTextureAsPng(pStagingTexture, filepath);

    // Cleanup
    pStagingTexture->Release();
    pBackBuffer->Release();
    pSwapChain->Release();
    pFactory->Release();
    pOutput->Release();
    pDXGIAdapter->Release();
    pDXGIDevice->Release();
    pDevice->Release();

    return true;
}

Upvotes: 0

Views: 148

Answers (0)

Related Questions