欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C++抓图服务

程序员文章站 2022-04-15 16:18:12
基于前两篇抓图文章,本文将抓图提取为一个服务,实现不同场景下抓图需求的封装。 C++使用BitBlt进行窗口抓图 C++使用PrintWindow进行窗口抓图 首先是抓图服务: ICaptureHelper.h #pragma once #include #include < ......

基于前两篇抓图文章,本文将抓图提取为一个服务,实现不同场景下抓图需求的封装。

c++使用bitblt进行窗口抓图

c++使用printwindow进行窗口抓图

首先是抓图服务:

icapturehelper.h 

#pragma once

#include <windows.h>
#include <string>
using std::string;

class icapturehelper
{
public:
    virtual ~icapturehelper() {}
    virtual bool init(const string& windowname) = 0;
    virtual bool init(hwnd hwnd) = 0;
    virtual void cleanup() = 0;
    virtual bool refreshwindow() = 0;
    virtual bool changewindowhandle(const string& windowname) = 0;
    virtual bool changewindowhandle(hwnd hwnd) = 0;
    virtual bool capture() = 0;

    virtual const rect& getwindowrect() const = 0;
    virtual const rect& getclientrect() const = 0;
    virtual int getbitmapdatasize() const = 0;
    virtual hbitmap getbitmap() const = 0;
    virtual void* getbitmapaddress() const = 0;
};

captureservice.h

#pragma once

#include "icapturehelper.h"
#include <map>
using std::map;

class captureservice
{
public:
    captureservice() = default;
    static captureservice& getinstance();

    enum capturetype
    {
        //使用createdibsection抓图,速度快,但是无法抓取d3d等渲染的窗口
        createdibsection = 0,

        //使用printwindow抓图,速度慢(16ms左右),但是可以抓取d3d等渲染的窗口
        printwindow
    };

    bool registercapture(string name, string windowname, capturetype type = createdibsection); //注册抓图服务
    bool registercapture(string name, hwnd hwnd, capturetype type = createdibsection); //注册抓图服务
    void unregistercapture(string name); //注销抓图服务
    bool isregister(string name); //获取是否已注册抓图服务

    bool refreshwindow(string name); //刷新窗口
    bool changewindowhandle(string name, string windowname); //修改窗口句柄
    bool changewindowhandle(string name, hwnd hwnd); //修改窗口句柄
    bool capture(string name); //抓图

    bool getwindowrect(string name, rect& winrect); //获取窗口尺寸
    bool getclientrect(string name, rect& clientrect); //获取窗口客户区尺寸
    bool getbitmapdatasize(string name, int& bmpdatasize); //获取抓图数据大小
    bool getbitmap(string name, hbitmap& bitmap); //获取窗口位图
    bool getbitmapaddress(string name, void** bitsptr); //获取窗口位图地址

    void cleanup(); //清理所有抓图服务

private:
    ~captureservice();

private:
    map<string, icapturehelper*> capturehelpers_;
};

captureservice.cpp

#include "stdafx.h"
#include "captureservice.h"
#include "dibcapturehelper.h"
#include "printcapturehelper.h"


captureservice::~captureservice()
{
    cleanup();
}

captureservice& captureservice::getinstance()
{
    static captureservice instance;
    return instance;
}

bool captureservice::registercapture(string name, string windowname, capturetype type /* = createdibsection */)
{
    const auto hwnd = ::findwindowa(nullptr, windowname.c_str());
    return registercapture(name, hwnd, type);
}

bool captureservice::registercapture(string name, hwnd hwnd, capturetype type /* = createdibsection */)
{
    if (name.empty() || capturehelpers_.find(name) != capturehelpers_.end())
    {
        return false;
    }

    icapturehelper* helper;
    switch (type)
    {
    case createdibsection:
        helper = new dibcapturehelper();
        break;
    case printwindow:
        helper = new printcapturehelper();
        break;
    default:
        return false;
    }

    if (helper == nullptr)
    {
        return false;
    }

    if (!helper->init(hwnd))
    {
        delete helper;
        return false;
    }

    capturehelpers_[name] = helper;
    return true;
}

void captureservice::unregistercapture(string name)
{
    if (name.empty() || capturehelpers_.find(name) == capturehelpers_.end())
    {
        return;
    }

    auto* capturehelper = capturehelpers_[name];
    if (capturehelper != nullptr)
    {
        capturehelper->cleanup();
        delete capturehelper;
    }

    capturehelpers_.erase(name);
}

bool captureservice::isregister(string name)
{
    return !name.empty() && capturehelpers_.find(name) != capturehelpers_.end();
}

bool captureservice::refreshwindow(string name)
{
    if (!isregister(name))
    {
        return false;
    }
    return capturehelpers_[name]->refreshwindow();
}

bool captureservice::changewindowhandle(string name, string windowname)
{
    if (!isregister(name))
    {
        return false;
    }
    return capturehelpers_[name]->changewindowhandle(windowname);
}

bool captureservice::changewindowhandle(string name, hwnd hwnd)
{
    if (!isregister(name))
    {
        return false;
    }
    return capturehelpers_[name]->changewindowhandle(hwnd);
}

bool captureservice::capture(string name)
{
    if (!isregister(name))
    {
        return false;
    }
    return capturehelpers_[name]->capture();
}

bool captureservice::getwindowrect(string name, rect& winrect)
{
    if (!isregister(name))
    {
        return false;
    }
    winrect = capturehelpers_[name]->getwindowrect();
    return true;
}

bool captureservice::getclientrect(string name, rect& clientrect)
{
    if (!isregister(name))
    {
        return false;
    }
    clientrect = capturehelpers_[name]->getclientrect();
    return true;
}

bool captureservice::getbitmapdatasize(string name, int& bmpdatasize)
{
    if (!isregister(name))
    {
        return false;
    }
    bmpdatasize = capturehelpers_[name]->getbitmapdatasize();
    return true;
}

bool captureservice::getbitmap(string name, hbitmap& bitmap)
{
    if (!isregister(name))
    {
        return false;
    }
    bitmap = capturehelpers_[name]->getbitmap();
    return true;
}

bool captureservice::getbitmapaddress(string name, void** bitsptr)
{
    if (!isregister(name))
    {
        return false;
    }
    *bitsptr = capturehelpers_[name]->getbitmapaddress();
    return true;
}

void captureservice::cleanup()
{
    for (auto iter = capturehelpers_.cbegin(); iter != capturehelpers_.cend(); ++iter)
    {
        auto* capturehelper = iter->second;
        if (capturehelper != nullptr)
        {
            capturehelper->cleanup();
            delete capturehelper;
        }
    }
    capturehelpers_.clear();
}

其次是抓图代码封装:

abscapturehelper.h

#pragma once

#include "icapturehelper.h"

class abscapturehelper : public icapturehelper
{
public:
    abscapturehelper();
    virtual ~abscapturehelper();

    bool init(const string& windowname) override;
    bool init(hwnd hwnd) override;
    void cleanup() override;
    bool refreshwindow() override;
    bool changewindowhandle(const string& windowname) override;
    bool changewindowhandle(hwnd hwnd) override;
    bool capture() override;

    const rect& getwindowrect() const override { return windowrect_; }
    const rect& getclientrect() const override { return clientrect_; }
    int getbitmapdatasize() const override { return bmpdatasize_; }
    hbitmap getbitmap() const override { return bitmap_; }
    void* getbitmapaddress() const override { return bitsptr_; }

protected:
    virtual bool initdc(const bitmapinfo& bitmapinfo) = 0;
    virtual bool docapture() = 0;

protected:
    hwnd hwnd_;
    hdc scrdc_;
    hdc memdc_;
    hbitmap bitmap_;
    hbitmap oldbitmap_;
    void* bitsptr_;

    rect windowrect_;
    rect clientrect_;
    int bmpdatasize_;
};

abscapturehelper.cpp

#include "stdafx.h"
#include "abscapturehelper.h"


abscapturehelper::abscapturehelper()
    : hwnd_(nullptr)
    , scrdc_(nullptr)
    , memdc_(nullptr)
    , bitmap_(nullptr)
    , oldbitmap_(nullptr)
    , bitsptr_(nullptr)
    , windowrect_{ 0, 0, 0, 0 }
    , clientrect_{ 0, 0, 0, 0 }
    , bmpdatasize_(0)
{
}

abscapturehelper::~abscapturehelper()
{
    abscapturehelper::cleanup();
}

bool abscapturehelper::init(const string& windowname)
{
    const auto handle = ::findwindowa(nullptr, windowname.c_str());
    if (handle == nullptr)
    {
        return false;
    }

    return init(handle);
}

bool abscapturehelper::init(hwnd hwnd)
{
    hwnd_ = hwnd;

    //获取窗口大小
    if (!::getwindowrect(hwnd_, &windowrect_) || !::getclientrect(hwnd_, &clientrect_))
    {
        return false;
    }

    const auto clientrectwidth = clientrect_.right - clientrect_.left;
    const auto clientrectheight = clientrect_.bottom - clientrect_.top;
    bmpdatasize_ = clientrectwidth * clientrectheight * 4;

    //位图信息
    bitmapinfo bitmapinfo;
    bitmapinfo.bmiheader.bisize = sizeof(bitmapinfo);
    bitmapinfo.bmiheader.biwidth = clientrectwidth;
    bitmapinfo.bmiheader.biheight = clientrectheight;
    bitmapinfo.bmiheader.biplanes = 1;
    bitmapinfo.bmiheader.bibitcount = 32;
    bitmapinfo.bmiheader.bisizeimage = clientrectwidth * clientrectheight;
    bitmapinfo.bmiheader.bicompression = bi_rgb;

    return initdc(bitmapinfo);
}

void abscapturehelper::cleanup()
{
    if (bitmap_ == nullptr)
    {
        return;
    }

    //删除用过的对象
    ::selectobject(memdc_, oldbitmap_);
    ::deleteobject(bitmap_);
    ::deletedc(memdc_);
    ::releasedc(hwnd_, scrdc_);

    hwnd_ = nullptr;
    scrdc_ = nullptr;
    memdc_ = nullptr;
    bitmap_ = nullptr;
    oldbitmap_ = nullptr;
    bitsptr_ = nullptr;
}

bool abscapturehelper::refreshwindow()
{
    const auto hwnd = hwnd_;
    cleanup();
    return init(hwnd);
}

bool abscapturehelper::changewindowhandle(const string& windowname)
{
    cleanup();
    return init(windowname);
}

bool abscapturehelper::changewindowhandle(hwnd hwnd)
{
    cleanup();
    return init(hwnd);
}

bool abscapturehelper::capture()
{
    if (bitmap_ == nullptr || memdc_ == nullptr || scrdc_ == nullptr)
    {
        return false;
    }

    return docapture();
}

dibcapturehelper.h

#pragma once

#include "abscapturehelper.h"

class dibcapturehelper : public abscapturehelper
{
public:
    dibcapturehelper();
    virtual ~dibcapturehelper();

protected:
    bool initdc(const bitmapinfo& bitmapinfo) override;
    bool docapture() override;

private:
    bool savebitmap_;
    int mockpagenumber;
    int bmpcount_;
};

dibcapturehelper.cpp

#include "stdafx.h"
#include "dibcapturehelper.h"
#include <sstream>

static int bmpcount = 0;
static int bmpmaxcount = 50;

dibcapturehelper::dibcapturehelper()
    : savebitmap_(false)
    , mockpagenumber(++bmpcount)
    , bmpcount_(0)
{
}

dibcapturehelper::~dibcapturehelper()
{
}

bool dibcapturehelper::initdc(const bitmapinfo& bitmapinfo)
{
    scrdc_ = ::getwindowdc(hwnd_);
    memdc_ = ::createcompatibledc(scrdc_);

    bitmap_ = ::createdibsection(memdc_, &bitmapinfo, dib_rgb_colors, &bitsptr_, nullptr, 0);
    if (bitmap_ == nullptr)
    {
        ::deletedc(memdc_);
        ::releasedc(hwnd_, scrdc_);
        return false;
    }

    oldbitmap_ = static_cast<hbitmap>(::selectobject(memdc_, bitmap_));
    return true;
}

bool dibcapturehelper::docapture()
{
    const auto clientrectwidth = clientrect_.right - clientrect_.left;
    const auto clientrectheight = clientrect_.bottom - clientrect_.top;

    const auto ret = ::bitblt(
        memdc_, 0, 0, clientrectwidth, clientrectheight,
        scrdc_, 0, 0, srccopy);

    return ret != 0;
}

printcapturehelper.h

#pragma once

#include "abscapturehelper.h"

class printcapturehelper : public abscapturehelper
{
public:
    printcapturehelper();
    virtual ~printcapturehelper();

protected:
    bool initdc(const bitmapinfo& bitmapinfo) override;
    bool docapture() override;
};

printcapturehelper.cpp

#include "stdafx.h"
#include "printcapturehelper.h"


printcapturehelper::printcapturehelper()
{
}

printcapturehelper::~printcapturehelper()
{
}

bool printcapturehelper::initdc(const bitmapinfo& bitmapinfo)
{
    scrdc_ = ::getwindowdc(hwnd_);
    memdc_ = ::createcompatibledc(scrdc_);

    bitmap_ = ::createdibsection(scrdc_, &bitmapinfo, dib_rgb_colors, &bitsptr_, nullptr, 0);
    if (bitmap_ == nullptr)
    {
        ::deletedc(memdc_);
        ::releasedc(hwnd_, scrdc_);
        return false;
    }
    
    oldbitmap_ = static_cast<hbitmap>(::selectobject(memdc_, bitmap_));
    return true;
}

bool printcapturehelper::docapture()
{
    const auto ret = ::printwindow(hwnd_, memdc_, pw_clientonly | pw_renderfullcontent);
    return ret != 0;
}