DX后台截图C++实现代码

文章仅发布于https://www.cnblogs.com/Icys/p/DXGI.html和知乎上。

传统的GDI API (BitBlt)虽然可以完美的完成后台截图的任务,但是归根结底效率还是太低。

直接使用DXGI方法截图只能完成前台窗口的截图,而DX HOOK的截图方法平添风险,以及很多场景不现实。

本文讲介绍使用 DwmGetDxSharedSurface 函数,优雅的完成后台截图的工作。

API介绍

函数定义
BOOL WINAPI DwmGetDxSharedSurface (
HWND hwnd,
HANDLE* phSurface,
LUID* pAdapterLuid,
ULONG* pFmtWindow,
ULONG* pPresentFlags,
ULONGLONG* pWin32kUpdateId
)

\(DwmGetDxSharedSurface\)来自于user32.dll(很离谱是吧,DwmApi不在DwmApi.dll里)。由于是ms没有公开的API,需要使用动态方法加载。

调用函数方法
//动态载入该函数
typedef HRESULT(WINAPI* DwmGetDxSharedSurface_t)(HWND, HANDLE*, LUID*, ULONG*, ULONG*, ULONGLONG*);
DwmGetDxSharedSurface_t DwmGetDxSharedSurface = NULL;
//获取地址
HMODULE hUser32 = LoadLibraryA("user32.dll");
if (hUser32 == NULL)
{
std::cout << "LoadLibraryA failed" << std::endl;
return 0;
}
DwmGetDxSharedSurface = (DwmGetDxSharedSurface_t)GetProcAddress(hUser32, "DwmGetDxSharedSurface");
//Dwm函数 在 user32.dll 中,真是离谱
if (DwmGetDxSharedSurface == NULL)
{
std::cout << "GetProcAddress failed" << std::endl;
return 0;
}
std::cout << DwmGetDxSharedSurface << std::endl;
参数含义
  • hwnd 被截图窗口的句柄
  • phSurface 被截图窗口的共享画面的句柄(应该是这么翻译吧)
  • 其他,暂时还没了解。

API调用

问题

显然这个API不能一步到位获得到BMP或者其他类型的图像数据。和BitBlt一样,这个API只是拿到了对应画面的副本(?,不清楚这样描述是否准确)。参照唯一有官方信息的API\(DwmDxGetWindowSharedSurface\),得到的是DX的一个对象,那就应该从DX下手。

初始化DX

这里讲个遇到的坑,DX设备的初始化不能在dllmain里进行,否则会失败。

HRESULT hr = S_OK;

hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory));
if (FAILED(hr))
{
throw "CreateDXGIFactory1 failed";
return 0;
}
pFactory->EnumAdapters(0, &pAdapter); const D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
}; D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featureLevels, 6, D3D11_SDK_VERSION, &device, NULL, NULL); if (device == NULL)
{
throw "D3D11CreateDevice failed";
return 0;
}
获取phSurface
HANDLE phSurface = NULL;
// 使用DWM截取屏幕
DwmGetDxSharedSurface(hWnd, &phSurface, NULL, NULL, NULL, NULL);
if (phSurface == NULL)
{
throw "Get Shared Surface Failded";
return 0;
}
将数据载入
HRESULT hr = S_OK;

ID3D11Texture2D* sharedSurface = NULL;
hr = device->OpenSharedResource(phSurface, __uuidof(ID3D11Texture2D), (void**)&sharedSurface);//打开对应资源
if (FAILED(hr))
{
throw "OpenSharedResource failed";
return 0;
} D3D11_TEXTURE2D_DESC shared_desc;
sharedSurface->GetDesc(&shared_desc); D3D11_TEXTURE2D_DESC description; description.ArraySize = 1;
description.BindFlags = 0;
description.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
description.Height = shared_desc.Height;
description.MipLevels = 1;
description.SampleDesc = { 1, 0 };
description.Usage = D3D11_USAGE_STAGING;
description.Width = shared_desc.Width;
description.MiscFlags = 0; hr = S_OK; ID3D11Texture2D* texture = NULL;
hr = device->CreateTexture2D(&description, NULL, &texture);
if (FAILED(hr))
{
sharedSurface->Release();
throw "CreateTexture2D failed";
return 0;
}
ID3D11DeviceContext* context = NULL;
device->GetImmediateContext(&context);
context->CopyResource(texture, sharedSurface); D3D11_MAPPED_SUBRESOURCE mappedResource;
context->Map(texture, 0, D3D11_MAP_READ, 0, &mappedResource);

这里我们其实就已经拿到了对应的图片资源

数据转化

根据DX设备填入的D3D11_CREATE_DEVICE_BGRA_SUPPORT。可以知

typedef struct D3D11_MAPPED_SUBRESOURCE {
void *pData;
UINT RowPitch;
UINT DepthPitch;
} D3D11_MAPPED_SUBRESOURCE;

其中的pData应该是一段对应像素排列位BGRA的位图。RowPitch是每行数据站的字长。为了方便我采用的是用OpenCV直接读入这段数据

cv::Mat mat(shared_desc.Height, shared_desc.Width, CV_8UC4, mappedResource.pData, mappedResource.RowPitch);
cv::imshow("mat", mat);
cv::waitKey(0);
//转BMP写出
std::vector<uchar> buffer;
cv::imencode(".bmp", mat, buffer);

当然也能用MFC

HBITMAP hbmp = CreateBitmap(shared desc.Width, shared desc.Height, 1 32, mappedResource.pData);
CImage img;
img.Attach(hbmp);
img.Save(L"233.bmp");
img.Detach();
DeleteObject(hbmp);

资源释放

最后别忘记了

context->Release();
texture->Release();
sharedSurface->Release(); device->Release();
pAdapter->Release();
pFactory->Release(); FreeLibrary(hUser32);

采用CloseHandle没法正常关掉phSurface,暂时不知道什么解决或方法,或是需不需要关掉

库的链接

用到了DX方面的库,当然要把他们的lib给链接上,在cpp文件中添加以下代码

#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")

问题

这个API截取不到标题栏。另外也可能是本人对API和DX的理解水平还不到位D2D/D3D渲染的窗口截图是全黑的。

DX后台截图C++实现代码的更多相关文章

  1. 基于java的后台截图功能的实现

    Java后台截图功能的实现 背景介绍: 在近期开发的可视化二期项目中的邮件项目中,邮件中的正文中含有图片.该图片的产生是将一些html网页转为图片格式,刚开始考虑使用第三方组件库html2image和 ...

  2. c++ 屏幕截图指定窗口句柄后台截图返回位图句柄

    /根据窗口句柄后台截图保存成BMP位图文件并且显示到picture 控件上 void GetScreenBmp(HWND hwnd, int left, int top, int width, int ...

  3. YbSoftwareFactory 代码生成插件【二十五】:Razor视图中以全局方式调用后台方法输出页面代码的三种方法

    上一篇介绍了 MVC中实现动态自定义路由 的实现,本篇将介绍Razor视图中以全局方式调用后台方法输出页面代码的三种方法. 框架最新的升级实现了一个页面部件功能,其实就是通过后台方法查询数据库内容,把 ...

  4. php截取后台登陆密码的代码

    php截取后台登陆密码的代码,很多时候黑客留了这样的代码,大家一定要注意下if($_POST[loginsubmit]!=){ //判断是否点了登陆按钮 $sb=user:.$_POST[userna ...

  5. 掌握GCD以及后台永久运行的代码 (使用GCD处理后台线程和UI线程的交互)

    一个例子: 在iPhone上做一个下载网页的功能,就是:在iPhone上放一个按钮,单击按钮时,显示一个转动的圆圈,表示正在进行下载,下载完成后,将内容加载到界面上的一个文本控件上. 使用GCD前: ...

  6. EasyUI + EF + MVC4 后台截图

    到目前完成的页面截图,完成了增删改查几项功能的技术测试,在解决几个小问题,就重新设计结构开始一个完整的后台开发,坚持用博客和云笔记记录开发过程.

  7. Yii2中后台用前台的代码设置验证码显示不出来?

    我说的是直接修改advanced模板.细心人会发现模板里在contact里有,登录也想要就仿照contact中的做法.前台好了,后台登录也要验证码,就把前台代码拿过来,可惜前后台的SiteContro ...

  8. ASP.NET后台输出js脚本代码

    利用asp.net输出js我们大多数都会直接使用Respone.Write()然后根js格式的代码,再在页面调用时我们直接这样是完全可以实现的,下面我来给大家介绍另一种方法 我是我最初的想法以下是代码 ...

  9. 如何在B2C电子商务网站后台添加CNZZ统计代码(转)

    CNZZ作为网站流量数据统计分析工具的一种,和百度统计工具类似,同样也是用于查看分析网站所有流量数据来源的一种站长工具,当然商家可根据自己的习惯在B2C电子商务网站后台添加相应的数据统计代码来管理您的 ...

  10. asp.net后台调用前台js代码

    为了通过ajax异步获取数据,我通过使用jquery的$(function{})方法,在页面加载时发送ajax请求,获取相关的数据.但是遇到了一个问题,当我发送的ajax请求需要根据传过来的URL参数 ...

随机推荐

  1. 【高并发】SimpleDateFormat类到底为啥不是线程安全的?(附六种解决方案,建议收藏)

    大家好,我是冰河~~ 首先问下大家:你使用的SimpleDateFormat类还安全吗?为什么说SimpleDateFormat类不是线程安全的?带着问题从本文中寻求答案. 提起SimpleDateF ...

  2. 无linux基础也能熟练掌握git的基本操作

    git是一个用来管理项目的工具,它的远程仓库有github.gitee.gitlab代码托管中心,既可以用于个人共享代码,又可以用于团队进行项目的协作与发布,那么我们一起来了解一下git该如何使用~ ...

  3. 聊聊 RocketMQ 名字服务

    NameServer 是专为 RocketMQ 设计的轻量级名字服务,它的源码非常精简,八个类 ,少于1000行代码. 这篇文章, 笔者会从基础概念.Broker发送心跳包.NameServer 维护 ...

  4. python命令行解析模块argparse

    argparse是Python标准库中推荐的命令行解析模块 code01: tmp.py import argparse parser = argparse.ArgumentParser(descri ...

  5. 三维模型OBJ格式轻量化压缩处理的数据质量提升方法分析

    三维模型OBJ格式轻量化压缩处理的数据质量提升方法分析 在三维模型的OBJ格式轻量化压缩处理过程中,除了减小文件大小和提高加载速度之外,我们也需要考虑如何提升数据质量.以下是几种常见的方法: 1.优化 ...

  6. Programming abstractions in C阅读笔记:p130-p131

    <Programming Abstractions In C>学习第52天,p130-p131,总结如下: 一.技术总结 1. pig latin game 通过pig latin gam ...

  7. 谁家面试往死里问 Swagger 啊?

    大家好,我是小富- 前言 说个挺奇葩的事,有个老铁给我发私信吐槽了一下它的面试经历,他去了个国企单位面试,然后面试官跟他就Swagger的问题聊了半个多小时.额- 面试嘛这些都不稀奇,总能遇到是千奇百 ...

  8. HarmonyOS扫码服务,应用服务一扫直达打造系统级流量新入口

    二维码如今是移动应用流量入口以及功能实现的重要工具,也是各App的流量入口,是物.人.服务的连接器,通过扫码我们可以更便捷的生活,更高效的进行信息交互,包括信息的发布.信息的获取. 在日常扫码过程中, ...

  9. elasticsearch wildcard 慢查询原因分析(深入到源码!!!)

    大家好,我是蓝胖子,前段时间线上elasticsearch集群遇到多次wildcard产生的性能问题, elasticsearch wildcard 一直是容易引发elasticsearch 容易宕机 ...

  10. 虚拟机问题:VMware Workstation 与 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard 后,可以运行

    解决办法:关闭Hyper-V功能. windows10使用VMware Workstation打开虚拟机时显示 VMware Workstation 与 Hyper-V 不兼容.请先从系统中移除 Hy ...