[C++]带动画鼠标指针的多屏采集和窗口采集,基于BitBlt和DXGI

2023-10-17 11:12

本文主要是介绍[C++]带动画鼠标指针的多屏采集和窗口采集,基于BitBlt和DXGI,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这个类被封装到DLL中引用,专门用来多屏或窗口图像采集,可用来编码成视频流做图像传输。

注意,如果你不想这个项目是个DLL才能运行,而是希望能放到非DLL项目中直接运行,请去掉头文件中的以下部分

#ifdef DESKTOPCAPTURE_EXPORTS
#define DESKTOPCAPTURE_API __declspec(dllexport)
#else
#define DESKTOPCAPTURE_API __declspec(dllimport)
#endif

如果你期望了解如何将带动画的鼠标指针绘制到图像上,那么请关注m_CursorFrame变量的使用。

那么,正式开始

首先来介绍下这个类应该如何使用,这里主要是演示了如何每30毫秒获取一张图像的效果,另外,如果你有Opencv库,那么可以解开Opencv相关的注释,可以直观地看到图像的显示效果。

#include <DesktopCapture.h>
//#include <opencv2/opencv.hpp>
//#include <opencv2/imgproc.hpp>
//#include <opencv2/imgproc/types_c.h>
//using namespace cv;
int main() {//使用BitBlt采集方法,采集默认屏幕DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::BitBlt, 0);//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::BitBlt, 1);//采集第一个屏幕//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::BitBlt, 2);//采集第二个屏幕//HWND hWindow = GetForegroundWindow();//获取最前窗口的句柄//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::BitBlt, hWindow, true);//采集窗口,包括他的标题边框//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::BitBlt, hWindow, false);//采集窗口,仅窗口中的内容//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::DXGI, 0);//DXGI工厂采集默认屏幕//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::DXGI, 1);//DXGI工厂采集第一个屏幕//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::DXGI, 2);//DXGI工厂采集第二个屏幕//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::DXGI, hWindow, true);//暂不支持//DesktopCapture DesktopCaptureClass(DesktopCapture::CapSrcType::DXGI, hWindow, false);//暂不支持//准备好获取图像帧的数据unsigned char* dataPtr = nullptr;int w, h;size_t size;//获取图像帧的宽高和大小,这里主要是获取大小DesktopCaptureClass.GetFrame(nullptr, w, h, size, true/*代表绘入鼠标*/);//根据回馈的大小分配内存到dataPtr中dataPtr = new unsigned char[size];while (true) {//获取一帧图像if (!DesktopCaptureClass.GetFrame(dataPtr, w, h, size, true)) {std::cerr << "获取图像帧失败" << std::endl;break;}//此时dataPtr存放好了图像,请使用该dataPtr//imshow("showMat", Mat(h, w, CV_8UC4, dataPtr));//waitKey(1);Sleep(30);}//回收内存空间delete[] dataPtr;dataPtr = nullptr;//清除类中的缓存变量,也可不写这句,析构也会自动调用DesktopCaptureClass.Clear();return 0;
}

可以看见,使用这个类非常简单,初始化变量后就立马就可以用一直使用GetFrame方法采集到图像帧,目前支持BitBlt和DXGI方式进行屏幕采集,而窗口采集目前仅支持用BItBlt来完成。

其中窗口采集的鼠标显示比较灵活,当采集的窗口不前置的时候,鼠标将不会被绘制到窗口上。

那么我们直接来观察源码,整个项目也只有这一个类,他的头文件和本体在文章末尾提供。

以下信息用于方便定位到相关的采集代码

关于BitBlt采集的核心方法是
GetFrameFromBitBlt
其中依赖的缓存变量来源于InitBitBlt()

关于DXGI采集的核心方法是
GetFrameFromDXGI
其中依赖的缓存变量来源于InitDXGI()

头文件:

#ifdef DESKTOPCAPTURE_EXPORTS
#define DESKTOPCAPTURE_API __declspec(dllexport)
#else
#define DESKTOPCAPTURE_API __declspec(dllimport)
#endif#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
#include <assert.h>#include <dshow.h>
#pragma comment(lib, "User32.lib")#include <d3d11.h>
#include <dxgi.h>
#include <dxgi1_5.h>
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "d3d11.lib")#define IS_NULL(POINTER) (nullptr == (POINTER))extern "C" {/// <summary>/// 桌面图像抓取类/// </summary>class DESKTOPCAPTURE_API DesktopCapture {//类的附属类型public:/// <summary>/// 消息类型/// </summary>enum class MessageType {Info, Warn, Error};/// <summary>/// 调试回调方法类型/// </summary>typedef void (*LogCallBack)(MessageType Type, const char* Message, const char* FunName, int Line);/// <summary>/// 采集来源类型/// </summary>enum class CapSrcType {BitBlt, DXGI};/// <summary>/// 采集目标类型/// </summary>enum class CapDstType {Window//窗口边框与内容, WindowContent//窗口内容, Screen//屏幕};//类的附属方法/// <summary>/// 获取对应采集类型的名称/// </summary>/// <param name="Type">采集类型</param>/// <returns>采集类型对应的名称</returns>const char* GetNameFromCapSourceType(CapSrcType Type);//类的公共方法public:/// <summary>/// 基于屏幕的图像采集的构造方法/// </summary>/// <param name="CapSourceNum">指定采集来源[0:BitBlt][1:DXGI]</param>/// <param name="ScreenNum">录制第几个屏幕,0是主屏,1是第一个屏幕,2是第二个屏幕</param>DesktopCapture(CapSrcType CapSrcType, int ScreenNum);/// <summary>/// 基于窗口的图像采集的构造方法/// </summary>/// <param name="CapSourceNum">指定采集来源[0:BitBlt][1:DXGI]</param>/// <param name="HWindow">采集指定窗口图像</param>/// <param name="IsDrawFrame">是否绘制窗口边框</param>DesktopCapture(CapSrcType CapSrcType, HWND HWindow, bool IsDrawFrame);/// <summary>/// 默认析构/// </summary>virtual ~DesktopCapture();/// <summary>/// 获取图像帧/// </summary>/// <param name="Frame">获取图像帧的数据指针,需要预先分配好空间,如果为null将不写入,一个合适的值可以通过调用一次该方法从Size上获取</param>/// <param name="Width">获取图像帧的宽</param>/// <param name="Height">获取图像帧的高</param>/// <param name="Size">获取图像帧的占用字节数</param>/// <param name="IsDrawCursor">是否绘入指针</param>/// <returns>获取成功与否</returns>bool GetFrame(unsigned char* Frame, int& Width, int& Height, size_t& Size, bool IsDrawCursor = false);/// <summary>/// 清除数据缓冲/// </summary>void Clear();/// <summary>/// 设置日志回调方法/// </summary>void SetLogCallBack(LogCallBack LogCallBack);//类的私有方法private:/// <summary>/// 获取图像帧/// </summary>/// <param name="Frame">存储用的图像帧,需要预先分配好空间,如果为null将不写入,一个合适的值可以通过调用一次该方法从Size上获取</param>/// <param name="IsDrawCursor">是否绘入指针</param>/// <returns>获取成功与否</returns>bool GetFrameFromBitBlt(unsigned char* Frame, int& Width, int& Height, size_t& Size, bool IsDrawCursor = false);/// <summary>/// 获取图像帧/// </summary>/// <param name="Frame">存储用的图像帧</param>/// <param name="IsDrawCursor">是否绘入指针</param>/// <returns>获取成功与否</returns>bool GetFrameFromDXGI(unsigned char* Frame, int& Width, int& Height, size_t& Size, bool IsDrawCursor = false);/// <summary>/// 安装BitBlt相关的变量/// </summary>/// <returns>安装成功与否</returns>bool  InitBitBlt();/// <summary>/// 卸载BitBlt相关的变量/// </summary>void UnInitBitBlt();/// <summary>/// 安装DXGI相关的变量/// </summary>/// <returns>安装成功与否</returns>bool  InitDXGI();/// <summary>/// 卸载DXGI相关的变量/// </summary>void UnInitDXGI();//BitBltstatic BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor,  // 显示器句柄HDC hdcMonitor,     // 监视器相关设备上下文的句柄LPRECT lprcMonitor, // 指向监视器相交矩形的指针LPARAM dwData       // 从EnumDisplayMonitors传递的数据);private:static LogCallBack m_LogFun;//日志回调方法指针const CapSrcType m_CapSrcType;//采集来源const CapDstType m_CapDstType;//采集目标const int m_ScreenNum;  //屏幕序号[0,...]const HWND m_HWindow;   //窗口句柄bool m_SeachStart{};//搜索是否开始int m_SeachNum{};//已经搜索的次数//Allint m_ScreenX{};//屏幕起始Xint m_ScreenY{};//屏幕起始Yint m_ScreenW{};//屏幕宽int m_ScreenH{};//屏幕高double m_ScreenZoom{ 1.0 };//屏幕缩放//BitBltHDC m_ScreenDC{};//屏幕DCHDC m_CompatibleDC{};//抓取后存储用的DCHBITMAP m_HBitmap{};//存储用的BitMapLONG m_DataSize{};//图像数据大小CURSORINFO m_CurInfo{};ICONINFO m_IconInfo{};clock_t m_CursorClockStart{};//鼠标动画开始时的时间const int m_CursorFrame{ 18 };//鼠标动画的帧数//DXGIIDXGIFactory1* m_Factory{ nullptr };IDXGIAdapter1* m_Adapter{ nullptr };IDXGIOutput* m_Output{ nullptr };DXGI_OUTPUT_DESC m_OutPutDesc{};DXGI_OUTDUPL_DESC m_OutPutlDesc{};IDXGIOutput1* m_Inner{ nullptr };ID3D11Device* m_Device{ nullptr };ID3D11DeviceContext* m_Context{ nullptr };IDXGIOutputDuplication* m_Duplication{ nullptr };bool mFastlane{};};
}

本体:

#include "pch.h"
#include "DesktopCapture.h"
#include <iostream>
#include <windows.h>using std::string;
using std::to_string;
using std::vector;#define LogFun(Type,Message) if(m_LogFun)m_LogFun((Type),(Message),__FUNCTION__,__LINE__)DesktopCapture::LogCallBack DesktopCapture::m_LogFun{ nullptr };const char* DesktopCapture::GetNameFromCapSourceType(CapSrcType Type)
{switch (Type) {case CapSrcType::BitBlt: {return "BitBlt";}case CapSrcType::DXGI: {return "DXGI";}default:LogFun(MessageType::Error, "未知参数引入");assert(false);return "";}
}DesktopCapture::DesktopCapture(CapSrcType CapSrcType, int ScreenNum):m_CapSrcType(CapSrcType), m_CapDstType(CapDstType::Screen), m_ScreenNum(ScreenNum), m_HWindow(nullptr)
{LogFun(MessageType::Info, (string("屏幕采集,采集来源设置为 ") + GetNameFromCapSourceType(m_CapSrcType)).c_str());
}DesktopCapture::DesktopCapture(CapSrcType CapSrcType, HWND HWindow, bool IsDrawFrame): m_CapSrcType(CapSrcType), m_CapDstType(IsDrawFrame ? CapDstType::WindowContent : CapDstType::Window), m_ScreenNum(-1), m_HWindow(HWindow)
{LogFun(MessageType::Info, (string("窗口采集,采集来源设置为 ") + GetNameFromCapSourceType(m_CapSrcType)).c_str());
}DesktopCapture::~DesktopCapture()
{Clear();
}bool DesktopCapture::GetFrame(unsigned char* Frame, int& Width, int& Height, size_t& Size, bool IsDrawCursor)
{switch (this->m_CapSrcType) {case CapSrcType::BitBlt: {return GetFrameFromBitBlt(Frame, Width, Height, Size, IsDrawCursor);}case CapSrcType::DXGI: {return GetFrameFromDXGI(Frame, Width, Height, Size, IsDrawCursor);}default: {LogFun(MessageType::Error, "未知参数引入");assert(false);return false;}}
}void DesktopCapture::Clear()
{UnInitBitBlt();UnInitDXGI();
}void DesktopCapture::SetLogCallBack(LogCallBack LogCallBack)
{m_LogFun = LogCallBack;
}bool DesktopCapture::GetFrameFromBitBlt(unsigned char* Frame, int& Width, int& Height, size_t& Size, bool IsDrawCursor)
{if (IS_NULL(m_HBitmap)) {if (!InitBitBlt()) {LogFun(MessageType::Error, "BitBlt装载失败");return false;}assert(m_HBitmap);}Width = m_ScreenW;Height = m_ScreenH;Size = m_DataSize;if (IS_NULL(Frame)) {return false;}if (!BitBlt(m_CompatibleDC, 0, 0, m_ScreenW, m_ScreenH, m_ScreenDC, 0, 0, SRCCOPY)) {LogFun(MessageType::Error, "BitBlt采集失败");return false;}if (IsDrawCursor && (IS_NULL(m_HWindow) || GetForegroundWindow() == m_HWindow)) {m_CurInfo.cbSize = sizeof(m_CurInfo);if (GetCursorInfo(&m_CurInfo)&& m_CurInfo.hCursor&& m_CurInfo.flags == CURSOR_SHOWING){//鼠标在屏幕之内if (m_CurInfo.ptScreenPos.x > m_ScreenX&& m_CurInfo.ptScreenPos.x < m_ScreenX + m_ScreenW&& m_CurInfo.ptScreenPos.y > m_ScreenY&& m_CurInfo.ptScreenPos.y < m_ScreenY + m_ScreenH) {if (m_CurInfo.hCursor) {if (GetIconInfo(m_CurInfo.hCursor, &m_IconInfo)) {DrawIconEx(m_CompatibleDC, static_cast<int>((m_CurInfo.ptScreenPos.x - m_IconInfo.xHotspot - m_ScreenX) * m_ScreenZoom), static_cast<int>((m_CurInfo.ptScreenPos.y - m_IconInfo.yHotspot - m_ScreenY) * m_ScreenZoom), m_CurInfo.hCursor, static_cast<int>(GetSystemMetrics(SM_CXCURSOR) * m_ScreenZoom), static_cast<int>(GetSystemMetrics(SM_CYCURSOR) * m_ScreenZoom), ((clock() - m_CursorClockStart) / 60) % m_CursorFrame, NULL, DI_NORMAL);}if (m_IconInfo.hbmMask)DeleteObject(m_IconInfo.hbmMask);if (m_IconInfo.hbmColor)DeleteObject(m_IconInfo.hbmColor);memset(&m_IconInfo, 0, sizeof(m_IconInfo));}}}memset(&m_CurInfo, 0, sizeof(m_CurInfo));}GetBitmapBits(m_HBitmap, m_DataSize, Frame);return true;
}bool DesktopCapture::GetFrameFromDXGI(unsigned char* Frame, int& Width, int& Height, size_t& Size, bool IsDrawCursor)
{if (IS_NULL(m_Duplication)) {if (!InitDXGI()) {LogFun(MessageType::Error, "DXGI装载失败");return false;}assert(m_Duplication);}Width = m_ScreenW;Height = m_ScreenH;Size = m_DataSize;if (IS_NULL(Frame)) {return false;}HRESULT hr;IDXGIResource* resource{ nullptr };DXGI_OUTDUPL_FRAME_INFO frameInfo;ID3D11Texture2D* texture{ nullptr };D3D11_TEXTURE2D_DESC textureDesc;ID3D11Texture2D* readable{ nullptr };IDXGISurface* surface{ nullptr };DXGI_MAPPED_RECT rect;unsigned char* finalFramePtr{ nullptr };hr = m_Duplication->AcquireNextFrame(/*timeoutInMilliseconds缓存的毫秒数*/30, &frameInfo, &resource);if (FAILED(hr)) {goto END_ERR;}//意味着图像还没更新if (frameInfo.LastPresentTime.QuadPart == 0) {goto END_ERR;}if (mFastlane) {hr = m_Duplication->MapDesktopSurface(&rect);finalFramePtr = rect.pBits;}else {hr = resource->QueryInterface(IID_ID3D11Texture2D, (void**)&texture);if (IS_NULL(texture)) {goto END_ERR;}texture->GetDesc(&textureDesc);textureDesc.Usage = D3D11_USAGE_STAGING;textureDesc.BindFlags = 0;textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;textureDesc.MiscFlags = 0;hr = m_Device->CreateTexture2D(&textureDesc,nullptr,&readable);if (FAILED(hr) || IS_NULL(readable)) {goto END_ERR;}readable->SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM);hr = readable->QueryInterface(IID_IDXGISurface,(void**)&surface);m_Context->CopyResource(readable, texture);hr = surface->Map(&rect, DXGI_MAP_READ);
#if FALSE //暂未利用画面旋转角度参数int rotate = m_OutPutlDesc.Rotation;switch (rotate) {case DXGI_MODE_ROTATION_IDENTITY | DXGI_MODE_ROTATION_UNSPECIFIED:rotate = 0;break;case DXGI_MODE_ROTATION_ROTATE90:rotate = 90;break;case DXGI_MODE_ROTATION_ROTATE180:rotate = 180;break;case DXGI_MODE_ROTATION_ROTATE270:rotate = 270;break;default:LogFun(MessageType::Error, ("Unknown rotation : " + std::to_string(rotate)).c_str());assert(0);}
#endiffinalFramePtr = rect.pBits;}if (IsDrawCursor) {// 创建一个设备上下文HDC hdc = GetDC(NULL);// 创建一个位图信息头BITMAPINFO bmi = { 0 };bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);bmi.bmiHeader.biWidth = Width;bmi.bmiHeader.biHeight = -Height;  // 负数表示图像数据从上到下bmi.bmiHeader.biPlanes = 1;bmi.bmiHeader.biBitCount = 32;bmi.bmiHeader.biCompression = BI_RGB;// 创建位图HBITMAP hBitmap = CreateDIBitmap(hdc, &(bmi.bmiHeader), CBM_INIT, finalFramePtr, &bmi, DIB_RGB_COLORS);HDC compatibleDC = CreateCompatibleDC(/*m_ScreenDC*/NULL);//使用了DC后,会导致看不到普通指针等指针图标没有了的现象if (IS_NULL(compatibleDC) || IS_NULL(hBitmap)) {LogFun(MessageType::Error, "申请DC或HBitmap失败");goto END_ERR;}SelectObject(compatibleDC, hBitmap);if (IS_NULL(m_HWindow) || GetForegroundWindow() == m_HWindow) {m_CurInfo.cbSize = sizeof(m_CurInfo);if (GetCursorInfo(&m_CurInfo)&& m_CurInfo.hCursor&& m_CurInfo.flags == CURSOR_SHOWING){//鼠标在屏幕之内if (m_CurInfo.ptScreenPos.x > m_ScreenX&& m_CurInfo.ptScreenPos.x < m_ScreenX + m_ScreenW&& m_CurInfo.ptScreenPos.y > m_ScreenY&& m_CurInfo.ptScreenPos.y < m_ScreenY + m_ScreenH) {if (m_CurInfo.hCursor) {if (GetIconInfo(m_CurInfo.hCursor, &m_IconInfo)) {DrawIconEx(compatibleDC, static_cast<int>((m_CurInfo.ptScreenPos.x - m_IconInfo.xHotspot - m_ScreenX) * m_ScreenZoom), static_cast<int>((m_CurInfo.ptScreenPos.y - m_IconInfo.yHotspot - m_ScreenY) * m_ScreenZoom), m_CurInfo.hCursor, static_cast<int>(GetSystemMetrics(SM_CXCURSOR) * m_ScreenZoom), static_cast<int>(GetSystemMetrics(SM_CYCURSOR) * m_ScreenZoom), ((clock() - m_CursorClockStart) / 60) % m_CursorFrame, NULL, DI_NORMAL);}if (m_IconInfo.hbmMask)DeleteObject(m_IconInfo.hbmMask);if (m_IconInfo.hbmColor)DeleteObject(m_IconInfo.hbmColor);memset(&m_IconInfo, 0, sizeof(m_IconInfo));}}}memset(&m_CurInfo, 0, sizeof(m_CurInfo));}GetBitmapBits(hBitmap, m_DataSize, Frame);// 清理资源DeleteDC(compatibleDC);DeleteObject(hBitmap);ReleaseDC(NULL, hdc);}else {memcpy_s(Frame, Size, finalFramePtr, Size);}
END_ERR:if (resource) {resource->Release();resource = nullptr;}m_Duplication->ReleaseFrame();if (mFastlane) {m_Duplication->UnMapDesktopSurface();}else if (surface) {surface->Unmap();}if (readable) {readable->Release();readable = nullptr;}if (texture) {texture->Release();texture = nullptr;}if (surface) {surface->Release();surface = nullptr;}return SUCCEEDED(hr);
}bool DesktopCapture::InitBitBlt()
{UnInitBitBlt();if (CapDstType::Window == m_CapDstType|| CapDstType::WindowContent == m_CapDstType) {RECT rect;if (IS_NULL(m_HWindow)) {LogFun(MessageType::Error, "窗口句柄为空");goto END_ERR;}if (CapDstType::Window == m_CapDstType) {if (!GetWindowRect(m_HWindow, &rect)) {LogFun(MessageType::Error, "获取窗口宽高失败");goto END_ERR;}}else {if (!GetClientRect(m_HWindow, &rect)) {LogFun(MessageType::Error, "获取窗口宽高失败");goto END_ERR;}}m_ScreenW = rect.right - rect.left;m_ScreenH = rect.bottom - rect.top;m_ScreenX = rect.left;m_ScreenY = rect.top;// 获取窗口所在的显示屏句柄HMONITOR monitor = MonitorFromWindow(m_HWindow, MONITOR_DEFAULTTONEAREST);MONITORINFOEX infoEx;memset(&infoEx, 0, sizeof(infoEx));infoEx.cbSize = sizeof(infoEx);if (!GetMonitorInfo(monitor, &infoEx)) {LogFun(MessageType::Error, "获取显示屏信息失败");goto END_ERR;}// 获取监视器物理宽高DEVMODE dm;dm.dmSize = sizeof(dm);dm.dmDriverExtra = 0;EnumDisplaySettings(infoEx.szDevice, ENUM_CURRENT_SETTINGS, &dm);m_ScreenZoom = dm.dmPelsWidth * 1.0 / (infoEx.rcMonitor.right - infoEx.rcMonitor.left);m_ScreenW = static_cast<int>(m_ScreenW * m_ScreenZoom);m_ScreenH = static_cast<int>(m_ScreenH * m_ScreenZoom);m_ScreenDC = GetWindowDC(m_HWindow);//如果没找到我们想要的屏幕,那就失败if (IS_NULL(m_ScreenDC)) {LogFun(MessageType::Error, "未能找到指定程序窗口");goto END_ERR;}}else if (CapDstType::Screen == m_CapDstType) {m_SeachStart = false;m_SeachNum = 0;while (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)this));//如果没找到我们想要的屏幕,那就失败if (IS_NULL(m_ScreenDC)) {LogFun(MessageType::Error, "未能找到指定屏幕");goto END_ERR;}}else {LogFun(MessageType::Error, "未知参数引入");assert(false);goto END_ERR;}m_DataSize = 4l * m_ScreenW * m_ScreenH;m_CompatibleDC = CreateCompatibleDC(/*m_ScreenDC*/NULL);//使用了DC后,会导致看不到普通指针等指针图标没有了的现象m_HBitmap = CreateCompatibleBitmap(m_ScreenDC, m_ScreenW, m_ScreenH);if (IS_NULL(m_CompatibleDC) || IS_NULL(m_HBitmap)) {LogFun(MessageType::Error, "申请DC或HBitmap失败");goto END_ERR;}SelectObject(m_CompatibleDC, m_HBitmap);goto END_FIN;
END_ERR:UnInitBitBlt();return false;
END_FIN:return true;
}void DesktopCapture::UnInitBitBlt()
{if (m_HBitmap) {DeleteObject(m_HBitmap);m_HBitmap = nullptr;}if (m_CompatibleDC) {DeleteDC(m_CompatibleDC);m_CompatibleDC = nullptr;}if (m_ScreenDC) {DeleteDC(m_ScreenDC);m_ScreenDC = nullptr;}
}bool DesktopCapture::InitDXGI()
{UnInitDXGI();HRESULT hr;if (CapDstType::Screen == m_CapDstType) {int adpaterNum = m_ScreenNum;if (adpaterNum > 0)--adpaterNum;hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&m_Factory);if (FAILED(hr) || IS_NULL(m_Factory)) {LogFun(MessageType::Error, ("无法创建DXGI工厂 错误代码:" + std::to_string(hr)).c_str());goto END_ERR;}while (S_OK == m_Factory->EnumAdapters1(adpaterNum, &m_Adapter) && m_Adapter) {int outputNum = 0;while (S_OK == m_Adapter->EnumOutputs(outputNum, &m_Output) && m_Output) {hr = m_Output->GetDesc(&m_OutPutDesc);if (SUCCEEDED(hr)) {bool is_primary = (m_OutPutDesc.DesktopCoordinates.left == 0 && m_OutPutDesc.DesktopCoordinates.top == 0);//如果我在寻找默认屏幕 或者 指定屏幕if ((0 == m_ScreenNum && is_primary) || (adpaterNum == m_ScreenNum - 1)) {m_Output->QueryInterface(IID_IDXGIOutput1, (void**)&m_Inner);if (FAILED(hr) || IS_NULL(m_Inner)) {LogFun(MessageType::Error, ("获取inner失败 错误代码:" + std::to_string(hr)).c_str());if (m_Inner) {m_Inner->Release();m_Inner = nullptr;}if (m_Output) {m_Output->Release();m_Output = nullptr;}++outputNum;continue;}m_Adapter->AddRef();goto END_FIN;}}else {LogFun(MessageType::Error, ("获取desc失败 错误代码:" + std::to_string(hr)).c_str());}if (m_Output) {m_Output->Release();m_Output = nullptr;}++outputNum;}if (m_Adapter) {m_Adapter->Release();m_Adapter = nullptr;}++adpaterNum;//如果指定屏幕打开失败if (m_ScreenNum > 0) {LogFun(MessageType::Error, "指定屏幕获取失败");goto END_ERR;}}LogFun(MessageType::Error, "无法创建任何adapte");goto END_ERR;}else {LogFun(MessageType::Error, "暂时不支持基于DXGI的程序窗口捕捉");return false;}END_ERR:UnInitDXGI();return false;
END_FIN:m_ScreenX = m_OutPutDesc.DesktopCoordinates.left;m_ScreenY = m_OutPutDesc.DesktopCoordinates.top;m_ScreenW = m_OutPutDesc.DesktopCoordinates.right - m_OutPutDesc.DesktopCoordinates.left;m_ScreenH = m_OutPutDesc.DesktopCoordinates.bottom - m_OutPutDesc.DesktopCoordinates.top;m_DataSize = 4l * m_ScreenW * m_ScreenH;//开始创建设备hr = D3D11CreateDevice(m_Adapter,D3D_DRIVER_TYPE_UNKNOWN,nullptr, // No software rasterizer.0,               // No m_Device flags.nullptr, // Feature levels.0,               // Feature levels' length.D3D11_SDK_VERSION,&m_Device,nullptr,&m_Context);if (FAILED(hr) || IS_NULL(m_Device) || IS_NULL(m_Context)) {LogFun(MessageType::Error, "无法创建任何device和context");goto END_ERR;}hr = m_Inner->DuplicateOutput(m_Device, &m_Duplication);if (FAILED(hr) || IS_NULL(m_Duplication)) {LogFun(MessageType::Error, "无法创建任何duplication");goto END_ERR;}m_Duplication->GetDesc(&m_OutPutlDesc);mFastlane = m_OutPutlDesc.DesktopImageInSystemMemory == TRUE;return true;
}void DesktopCapture::UnInitDXGI()
{if (m_Duplication) {m_Duplication->ReleaseFrame();if (mFastlane) {m_Duplication->UnMapDesktopSurface();}m_Duplication->Release();m_Duplication = nullptr;}if (m_Context) {m_Context->Release();m_Context = nullptr;}if (m_Device) {m_Device->Release();m_Device = nullptr;}if (m_Inner) {m_Inner->Release();m_Inner = nullptr;}if (m_Output) {m_Output->Release();m_Output = nullptr;}if (m_Adapter) {m_Adapter->Release();m_Adapter = nullptr;}if (m_Factory) {m_Factory->Release();m_Factory = nullptr;}
}BOOL DesktopCapture::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{DesktopCapture* thisPtr = (DesktopCapture*)dwData;MONITORINFOEX infoEx;memset(&infoEx, 0, sizeof(infoEx));infoEx.cbSize = sizeof(infoEx);if (!GetMonitorInfo(hMonitor, &infoEx)) {LogFun(MessageType::Error, "获取显示屏信息失败");return false;}if (!thisPtr->m_SeachStart) {//如果搜索还没开始,那么寻找主屏幕if (MONITORINFOF_PRIMARY == infoEx.dwFlags) {thisPtr->m_SeachStart = true;}else {return true;}}//来到这里意味着搜索已经开始了//如果这里是主屏if (infoEx.dwFlags == MONITORINFOF_PRIMARY) {//如果我要获取的是主屏if (0 == thisPtr->m_ScreenNum) {goto END_FIN;}//如果循环回来了主屏,仍找不到指定屏幕if (thisPtr->m_SeachNum > 0) {return false;}}//如果我要获取的是指定屏幕,且这里就是指定屏幕if (++thisPtr->m_SeachNum == thisPtr->m_ScreenNum) {goto END_FIN;}//继续寻找return true;END_FIN://获取屏幕DCthisPtr->m_ScreenDC = CreateDC(L"DISPLAY", infoEx.szDevice, NULL, NULL);// 获取监视器物理宽高DEVMODE dm;dm.dmSize = sizeof(dm);dm.dmDriverExtra = 0;EnumDisplaySettings(infoEx.szDevice, ENUM_CURRENT_SETTINGS, &dm);thisPtr->m_ScreenW = dm.dmPelsWidth;thisPtr->m_ScreenH = dm.dmPelsHeight;thisPtr->m_ScreenX = infoEx.rcMonitor.left;thisPtr->m_ScreenY = infoEx.rcMonitor.top;thisPtr->m_ScreenZoom = thisPtr->m_ScreenW * 1.0 / (infoEx.rcMonitor.right - infoEx.rcMonitor.left);return false;
}

这篇关于[C++]带动画鼠标指针的多屏采集和窗口采集,基于BitBlt和DXGI的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/224999

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

使用C/C++调用libcurl调试消息的方式

《使用C/C++调用libcurl调试消息的方式》在使用C/C++调用libcurl进行HTTP请求时,有时我们需要查看请求的/应答消息的内容(包括请求头和请求体)以方便调试,libcurl提供了多种... 目录1. libcurl 调试工具简介2. 输出请求消息使用 CURLOPT_VERBOSE使用 C

C++实现获取本机MAC地址与IP地址

《C++实现获取本机MAC地址与IP地址》这篇文章主要为大家详细介绍了C++实现获取本机MAC地址与IP地址的两种方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实际工作中,项目上常常需要获取本机的IP地址和MAC地址,在此使用两种方案获取1.MFC中获取IP和MAC地址获取