概述

  很多软件都需要屏幕捕捉功能,在软件中实现屏幕捕捉也不是难事,在微软Windows平台,有很多截屏的方法,例如:BitBlt、Mirror driver、 GDI hook、DirectX、DWM/Dxgi hook、Desktop Duplication与GetWindowDC 等方法,但大多效率不高,效率高的 Mirror driver技术只能用于XP等老系统,在Windows8 与Windows 10 上似乎已经失效,Windows8以后微软引入了一套新的接口,叫“Desktop Duplication API”,应用程序可以通过这套API访问桌面数据。
      Desktop Duplication API是通过Microsoft DirectX Graphics Infrastructure (DXGI)来提供桌面图像的,速度非常快。DXGI是通过GPU实现的,因此cpu占用率很低,性能非常高。 Duplication API获取到的桌面数据,不管显示模式如何设置,都永远是32位RGBA数据,这就给屏幕捕捉带来了很大的方便性,不再需要考虑各种显示模式的问题了。

注意:本捕捉方法只能用于Windows 10;Windows 7 系统的DX查询不到相关接口,所以不能用的。

要实现DXGI屏幕捕捉,基本流程如下:

1)创建D3DDevice;
2)通过一系列接口获取路径,获取到 IDXGIOutputDuplication 接口;
3)调用AcquireNextFrame,获取当前桌面数据,保存在 IDXGIResource 中;
4)把数据从GPU映射到内存中拷贝需要的数据到自己的buffer里。

其中,获取到 IDXGIOutputDuplication 接口,是通过如下路径:
IDXGIDevice --> IDXGIAdapter --> IDXGIOutput --> IDXGIOutput1 --> IDXGIOutputDuplication

真实实现DXGI屏幕捕捉的代码如下:

//
//DXGICaptor.h
//#include <d3d11.h>
#include <dxgi1_2.h>class VideoDXGICaptor
{
public:VideoDXGICaptor();~VideoDXGICaptor();public:BOOL Init();VOID Deinit();public:virtual BOOL CaptureImage(RECT &rect, void *pData, INT &nLen);virtual BOOL CaptureImage(void *pData, INT &nLen);virtual BOOL ResetDevice();private:BOOL  AttatchToThread(VOID);BOOL  QueryFrame(void *pImgData, INT &nImgSize);BOOL  QueryFrame(void *pImgData, INT &nImgSize, int z);private:IDXGIResource *zhDesktopResource;DXGI_OUTDUPL_FRAME_INFO zFrameInfo;ID3D11Texture2D *zhAcquiredDesktopImage;IDXGISurface *zhStagingSurf;private:BOOL                    m_bInit;int                     m_iWidth, m_iHeight;ID3D11Device           *m_hDevice;ID3D11DeviceContext    *m_hContext;IDXGIOutputDuplication *m_hDeskDupl;DXGI_OUTPUT_DESC        m_dxgiOutDesc;
};//
//DXGICaptor.cpp
//#include "stdafx.h"
#include "DXGICaptor.h"
#include <windows.h>
#include <gdiplus.h>#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")#define RESET_OBJECT(obj) { if(obj) obj->Release(); obj = NULL; }
static BOOL g_bAttach = FALSE;VideoDXGICaptor::VideoDXGICaptor()
{m_bInit = FALSE;m_hDevice = NULL;m_hContext = NULL;m_hDeskDupl = NULL;ZeroMemory(&m_dxgiOutDesc, sizeof(m_dxgiOutDesc));
}
VideoDXGICaptor::~VideoDXGICaptor()
{Deinit();
}
BOOL VideoDXGICaptor::Init()
{HRESULT hr = S_OK;if (m_bInit){return FALSE;}// Driver types supportedD3D_DRIVER_TYPE DriverTypes[] ={D3D_DRIVER_TYPE_HARDWARE,D3D_DRIVER_TYPE_WARP,D3D_DRIVER_TYPE_REFERENCE,};UINT NumDriverTypes = ARRAYSIZE(DriverTypes);// Feature levels supportedD3D_FEATURE_LEVEL FeatureLevels[] ={D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_10_1,D3D_FEATURE_LEVEL_10_0,D3D_FEATURE_LEVEL_9_1};UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);D3D_FEATURE_LEVEL FeatureLevel;//// Create D3D device//for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex){hr = D3D11CreateDevice(NULL, DriverTypes[DriverTypeIndex], NULL, 0, FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &m_hDevice, &FeatureLevel, &m_hContext);if (SUCCEEDED(hr)){break;}}if (FAILED(hr)){return FALSE;}//// Get DXGI device//IDXGIDevice *hDxgiDevice = NULL;hr = m_hDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&hDxgiDevice));if (FAILED(hr)){return FALSE;}//// Get DXGI adapter//IDXGIAdapter *hDxgiAdapter = NULL;hr = hDxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&hDxgiAdapter));RESET_OBJECT(hDxgiDevice);if (FAILED(hr)){return FALSE;}//// Get output//INT nOutput = 0;IDXGIOutput *hDxgiOutput = NULL;hr = hDxgiAdapter->EnumOutputs(nOutput, &hDxgiOutput);RESET_OBJECT(hDxgiAdapter);if (FAILED(hr)){return FALSE;}//// get output description struct//hDxgiOutput->GetDesc(&m_dxgiOutDesc);//// QI for Output 1//IDXGIOutput1 *hDxgiOutput1 = NULL;hr = hDxgiOutput->QueryInterface(__uuidof(hDxgiOutput1), reinterpret_cast<void**>(&hDxgiOutput1));RESET_OBJECT(hDxgiOutput);if (FAILED(hr)){return FALSE;}//// Create desktop duplication//hr = hDxgiOutput1->DuplicateOutput(m_hDevice, &m_hDeskDupl);RESET_OBJECT(hDxgiOutput1);if (FAILED(hr)){return FALSE;}// 初始化成功m_bInit = TRUE;return TRUE;
// #else// 小于vs2012,此功能不能实现return FALSE;
// #endif
}
VOID VideoDXGICaptor::Deinit()
{if (!m_bInit){return;}m_bInit = FALSE;if (m_hDeskDupl){m_hDeskDupl->Release();m_hDeskDupl = NULL;}if (m_hDevice){m_hDevice->Release();m_hDevice = NULL;}if (m_hContext){m_hContext->Release();m_hContext = NULL;}
}BOOL VideoDXGICaptor::AttatchToThread(VOID)
{if (g_bAttach){return TRUE;}HDESK hCurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL);if (!hCurrentDesktop){return FALSE;}// Attach desktop to this threadBOOL bDesktopAttached = SetThreadDesktop(hCurrentDesktop);CloseDesktop(hCurrentDesktop);hCurrentDesktop = NULL;g_bAttach = TRUE;return bDesktopAttached;
}BOOL VideoDXGICaptor::CaptureImage(RECT &rect, void *pData, INT &nLen)
{return QueryFrame(pData, nLen);
}
BOOL VideoDXGICaptor::CaptureImage(void *pData, INT &nLen)
{return QueryFrame(pData, nLen);
}
BOOL VideoDXGICaptor::ResetDevice()
{Deinit();return Init();
}
BOOL VideoDXGICaptor::QueryFrame(void *pImgData, INT &nImgSize)
{if (!m_bInit || !AttatchToThread()){return FALSE;}nImgSize = 0;IDXGIResource *hDesktopResource = NULL;DXGI_OUTDUPL_FRAME_INFO FrameInfo;HRESULT hr = m_hDeskDupl->AcquireNextFrame(0, &FrameInfo, &hDesktopResource);if (FAILED(hr)){//// 在一些win10的系统上,如果桌面没有变化的情况下,;// 这里会发生超时现象,但是这并不是发生了错误,而是系统优化了刷新动作导致的。;// 所以,这里没必要返回FALSE,返回不带任何数据的TRUE即可;//return TRUE;}//// query next frame staging buffer//ID3D11Texture2D *hAcquiredDesktopImage = NULL;hr = hDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&hAcquiredDesktopImage));RESET_OBJECT(hDesktopResource);if (FAILED(hr)){return FALSE;}//// copy old description//D3D11_TEXTURE2D_DESC frameDescriptor;hAcquiredDesktopImage->GetDesc(&frameDescriptor);//// create a new staging buffer for fill frame image//ID3D11Texture2D *hNewDesktopImage = NULL;frameDescriptor.Usage = D3D11_USAGE_STAGING;frameDescriptor.CPUAccessFlags = D3D11_CPU_ACCESS_READ;frameDescriptor.BindFlags = 0;frameDescriptor.MiscFlags = 0;frameDescriptor.MipLevels = 1;frameDescriptor.ArraySize = 1;frameDescriptor.SampleDesc.Count = 1;hr = m_hDevice->CreateTexture2D(&frameDescriptor, NULL, &hNewDesktopImage);if (FAILED(hr)){RESET_OBJECT(hAcquiredDesktopImage);m_hDeskDupl->ReleaseFrame();return FALSE;}//// copy next staging buffer to new staging buffer//m_hContext->CopyResource(hNewDesktopImage, hAcquiredDesktopImage);RESET_OBJECT(hAcquiredDesktopImage);m_hDeskDupl->ReleaseFrame();//// create staging buffer for map bits//IDXGISurface *hStagingSurf = NULL;hr = hNewDesktopImage->QueryInterface(__uuidof(IDXGISurface), (void **)(&hStagingSurf));RESET_OBJECT(hNewDesktopImage);if (FAILED(hr)){return FALSE;}//// copy bits to user space//DXGI_MAPPED_RECT mappedRect;hr = hStagingSurf->Map(&mappedRect, DXGI_MAP_READ);if (SUCCEEDED(hr)){// nImgSize = GetWidth() * GetHeight() * 3;// PrepareBGR24From32(mappedRect.pBits, (BYTE*)pImgData, m_dxgiOutDesc.DesktopCoordinates);// mappedRect.pBits;// am_dxgiOutDesc.DesktopCoordinates;memcpy((BYTE*)pImgData, mappedRect.pBits, m_dxgiOutDesc.DesktopCoordinates.right * m_dxgiOutDesc.DesktopCoordinates.bottom * 4);hStagingSurf->Unmap();}RESET_OBJECT(hStagingSurf);return SUCCEEDED(hr);
}

Windows 10 下的高效抓屏方法相关推荐

  1. 解决雷电模拟器在windows 10下无法桥接网卡的问题

    https://www.ldmnq.com/forum/thread-60719-1-1.html https://www.renyiwei.com/archives/1947.html 解决雷电模拟 ...

  2. w ndows10玩游戏蓝屏,Windows 10 电脑玩穿越火线蓝屏原因及解决方法

    Windows 10 蓝屏是非常常见的,可是面对不同原因出现的蓝屏您又知道如何去处理吗?今天我们就来通过解决穿越火线蓝屏顺便一起看看都是因为什么原因导致的 Windows 10 系统蓝屏吧. 1.显卡 ...

  3. windows 10下搭建pyspark与遇到的一些问题的解决方法

    目录 windows 10 下 搭建 pyspark 所需要的工具 过程与步骤 windows 10 下 搭建 pyspark 所需要的工具 Java JDK 1.8.0 spark-2.2.0-bi ...

  4. 安装 | Windows 10下基于Anaconda的TensorFlow详细安装步骤(续)——Pycharm运行tensorflow

    github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 Blogger:MichaelBeecha ...

  5. Windows 10 下 VS2017(+Clion) C/C++ 配置 OpenCV-4.4.0

    VS2017(+Clion) 配置 OpenCV-4.4.0 我的小站.Github OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视 ...

  6. windows 10下的kiosk模式

    windows 10下的kiosk模式可以保证windows 10 开机自动运行某个程序,且全屏,除了按alt + del + ctrl组合键退出外,按鼠标.键盘不能见到任何window系统下的任何界 ...

  7. windows7黑屏修复_如何在Windows 10更新后修复黑屏

    windows7黑屏修复 RealVector/Shutterstock.comRealVector / Shutterstock.com Some Windows 10 PCs have been ...

  8. mingw版本下qt与HTML,QT5.10开发(2) 在Windows 10下使用MinGW编译 静态Qt 5.10 release版 详细过程...

    Qt建议安装动态链接Dbug版和编译安装静态链接release版 前提: 先安装动态链接Dbug版,方法:QT5.10开发(1)安装QT5.10 地址:http://blog.csdn.net/qq_ ...

  9. Windows 10家庭版启用远程桌面的方法

    Windows 10家庭版启用远程桌面的方法 本文更新2021-01-26,针对最新Windows 10更新和验证. 背景: Windows 10家庭版不支持远程桌面服务器功能,微软对其进行了限制,只 ...

最新文章

  1. 看过来,包邮送AirPods Pro!
  2. BIOS英文报错详解;你虽会做系统,但你会看BIOS英文报错吗,仅供大家参考学习。...
  3. 合种樟子松/华山松专车3天领证
  4. python新闻系统_干货 | Python 实现新闻系统内容的增删改查功能
  5. Android提供的LruCache类简介
  6. mysql5.7 解压版 中文乱码_MySQL 5.7解压版安装、卸载及乱码问题的图文解决方法...
  7. 【脑经急转弯】—— 猜额头上的数字
  8. Unity 2D 闪电特效
  9. 药剂师揭露中药行业内幕:代煎多偷工减料
  10. ovn 通过网关虚拟路由器连接外部网络
  11. VLAN的划分以及三层交换机理论,OMG,太详细了吧,看它!
  12. 征集难于处理的机械臂奇异点位
  13. VC++年月日时间和64位时间的使用及相互转换(附源码)
  14. 今年 NFT 爆火,如何快速入行?(艺术家完整指南)
  15. 书法拓片matlab,[转载]碑帖拓片摹拓技法
  16. ISCA2011第二篇文献:Virtualizing Performance Asymmetric Multi-core Systems
  17. 意甲06-07赛季赛程出炉
  18. iphone开发的几个Apple官方中文教程地址
  19. koa2 mysql 中间件_Koa2——中间件
  20. 《传热学》王厚华笔记和课后习题答案

热门文章

  1. Nachos文件系统目录树实现
  2. 商场三十六计——第14计 “借尸还魂”
  3. bars 除障句完整_柏拉图的80句格言,汇成了《有光亮的地方就会有阴影》这本书...
  4. 记vue中如何使用better-scroll滚动插件
  5. html5-表格练习题
  6. bright-domain解决园区网内二层网络中不同VLAN间互访
  7. 解决逃离塔科夫0.12.9离线版修改商人可回收所有物品的问题
  8. 电脑网速很快但网页打开很慢
  9. 六神磊磊读唐诗中的敏捷:(三)晚唐:知遇
  10. 2022-2028年中国考前英语培训行业市场现状调查及投资商机预测报告