C++によるPrintWindowを用いたウィンドウキャプチャの実装

本実装では、C++とWindows APIを使用して特定のウィンドウのスクリーンショットを取得します。CreateDIBSection関数により、アプリケーションが直接書き込めるデバイス非依存ビットマップ(DIB)を作成し、メモリ内のビットマップデータへのポインタを提供します。

PrintWindow関数はD3Dでレンダリングされるウィンドウ(ExcelやWindows 10のビデオプレイヤーなど)のキャプチャが可能です。通常のウィンドウをキャプチャする場合は、WindowsがD3Dで描画するウィンドウシャドウも含まれて取得されます。

1、WindowCaptureUtility.h

#pragma once

#include <windows.h>
#include <string>

class WindowCaptureUtility
{
public:
    WindowCaptureUtility();
    ~WindowCaptureUtility();

    bool Initialize(const std::string& targetWindowName);
    bool Initialize(HWND windowHandle);
    void ReleaseResources();
    bool UpdateWindowInfo();
    bool SwitchTargetWindow(const std::string& newWindowName);
    bool SwitchTargetWindow(HWND newWindowHandle);
    bool PerformCapture() const;

    const RECT& GetWindowDimensions() const { return windowDimensions_; }
    const RECT& GetClientDimensions() const { return clientDimensions_; }
    int GetBitmapMemorySize() const { return bitmapMemorySize_; }
    HBITMAP GetBitmapHandle() const { return bitmapHandle_; }
    void* GetBitmapBuffer() const { return pixelBuffer_; }

private:
    HWND targetWindow_;
    HDC screenDC_;
    HDC memoryDC_;
    HBITMAP bitmapHandle_;
    HBITMAP previousBitmap_;
    void* pixelBuffer_;

    RECT windowDimensions_;
    RECT clientDimensions_;
    int bitmapMemorySize_;
};

2、WindowCaptureUtility.cpp

#include "WindowCaptureUtility.h"

WindowCaptureUtility::WindowCaptureUtility()
    : targetWindow_(nullptr)
    , screenDC_(nullptr)
    , memoryDC_(nullptr)
    , bitmapHandle_(nullptr)
    , previousBitmap_(nullptr)
    , pixelBuffer_(nullptr)
    , windowDimensions_{ 0, 0, 0, 0 }
    , clientDimensions_{ 0, 0, 0, 0 }
    , bitmapMemorySize_(0)
{
}

WindowCaptureUtility::~WindowCaptureUtility()
{
    ReleaseResources();
}

bool WindowCaptureUtility::Initialize(const std::string& targetWindowName)
{
    HWND windowHandle = ::FindWindowA(nullptr, targetWindowName.c_str());
    if (windowHandle == nullptr)
        return false;

    return Initialize(windowHandle);
}

bool WindowCaptureUtility::Initialize(HWND windowHandle)
{
    targetWindow_ = windowHandle;

    if (!::GetWindowRect(targetWindow_, &windowDimensions_) || 
        !::GetClientRect(targetWindow_, &clientDimensions_))
    {
        return false;
    }

    const int width = clientDimensions_.right - clientDimensions_.left;
    const int height = clientDimensions_.bottom - clientDimensions_.top;
    bitmapMemorySize_ = width * height * 4;

    BITMAPINFO bmpInfo = {};
    bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfo.bmiHeader.biWidth = width;
    bmpInfo.bmiHeader.biHeight = height;
    bmpInfo.bmiHeader.biPlanes = 1;
    bmpInfo.bmiHeader.biBitCount = 32;
    bmpInfo.bmiHeader.biCompression = BI_RGB;

    screenDC_ = ::GetWindowDC(targetWindow_);
    memoryDC_ = ::CreateCompatibleDC(screenDC_);
    bitmapHandle_ = ::CreateDIBSection(screenDC_, &bmpInfo, DIB_RGB_COLORS, &pixelBuffer_, nullptr, 0);
    
    if (bitmapHandle_ == nullptr)
    {
        ::DeleteDC(memoryDC_);
        ::ReleaseDC(targetWindow_, screenDC_);
        return false;
    }

    previousBitmap_ = (HBITMAP)::SelectObject(memoryDC_, bitmapHandle_);
    return true;
}

void WindowCaptureUtility::ReleaseResources()
{
    if (bitmapHandle_ == nullptr)
        return;

    ::SelectObject(memoryDC_, previousBitmap_);
    ::DeleteObject(bitmapHandle_);
    ::DeleteDC(memoryDC_);
    ::ReleaseDC(targetWindow_, screenDC_);

    targetWindow_ = nullptr;
    screenDC_ = nullptr;
    memoryDC_ = nullptr;
    bitmapHandle_ = nullptr;
    previousBitmap_ = nullptr;
}

bool WindowCaptureUtility::UpdateWindowInfo()
{
    HWND currentWindow = targetWindow_;
    ReleaseResources();
    return Initialize(currentWindow);
}

bool WindowCaptureUtility::SwitchTargetWindow(const std::string& newWindowName)
{
    ReleaseResources();
    return Initialize(newWindowName);
}

bool WindowCaptureUtility::SwitchTargetWindow(HWND newWindowHandle)
{
    ReleaseResources();
    return Initialize(newWindowHandle);
}

bool WindowCaptureUtility::PerformCapture() const
{
    if (bitmapHandle_ == nullptr || memoryDC_ == nullptr || screenDC_ == nullptr)
        return false;

    BOOL result = ::PrintWindow(targetWindow_, memoryDC_, PW_CLIENTONLY | PW_RENDERFULLCONTENT);
    return result != FALSE;
}

タグ: C++ Windows API PrintWindow CreateDIBSection スクリーンショット

5月16日 08:21 投稿