首页 > 编程语言 >DX后台截图C++实现代码

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

时间:2023-11-19 20:57:42浏览次数:37  
标签:截图 description C++ API DX D3D11 NULL

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渲染的窗口截图是全黑的。

标签:截图,description,C++,API,DX,D3D11,NULL
From: https://www.cnblogs.com/Icys/p/DXGI.html

相关文章

  • 建设招商农业邮政工商一体化模拟器,回执单转账余额截图都可以,JAVA模拟代码!
    闲着用JAVA研究了一个模拟器,但是我加了水印的,这个你做不了啥坏事,主要就是学习一下我写的代码和代码的实现逻辑,包括“主类和模块”还有“截图生成模块”以及“信息的输入和处理”三大模块,说复杂不复杂,说简单也不简单,下面框架图是网上找的,需要和代码相互结合才能实现具体的效果。UI......
  • C++AVL树和红黑树的模拟实现
    前言在二叉树的基础上,为了让搜索更加快捷,出现的二叉搜索树,二叉搜索树规定,二叉树的左子树的值一定都小于其父亲节点的值,所有右子树的值一定都大于其父亲节点的值,这样就保证了在查找某一个数据时,让时间复杂度最低为变为logn。一、二叉树两种特殊的二叉树1.满二叉树满二叉树每层的节......
  • 拼多多订单生成器手机版,支持淘宝京东截图生成,E4A源码,仅供娱乐学习
    闲着用E4A对接了JAVA类库制作了一个订单生成器,当然我叫了水印,这个软件或者里面的截图做不了啥坏事,仅仅用来学习娱乐装逼用的,下面是框架和代码。框架图1:  框架图2:  JAVa代码库:======================================================//商品类classProduct{St......
  • P2240 【深基12.例1】部分背包问题(C/C++)
    P2240【深基12.例1】部分背包问题先把物品按照单位重量的价值降序排序,然后依次装入背包。如果背包容量不小于当前要装的物品重量,就全部装入,如果小于,那就剩余多少容量就装多少容量的当前物品。#include<bits/stdc++.h>usingnamespacestd;structjinbi{ doublem; doublev;......
  • C/C++ 获取主机网卡MAC地址
    MAC地址(MediaAccessControladdress),又称为物理地址或硬件地址,是网络适配器(网卡)在制造时被分配的全球唯一的48位地址。这个地址是数据链路层(OSI模型的第二层)的一部分,用于在局域网(LAN)中唯一标识网络设备。获取网卡地址主要用于网络标识和身份验证的目的。MAC地址是一个唯一的硬件......
  • 手机股票持仓截图生成器,同花顺收益图生成器,交割单都支持,开源版分享!
    这个源码是别人的,之前估计分享过是网上买的,算是定制的,源码是2023年6月份开发出来的,但是现在项目早过了,这个也就没用了,然后就把作者写的纯源码全盘分享出来,方便大家研究一下这个源码一些好的结构设计和开发思路,支持的功能很多比如交割单、BS点都支持,还能生成手机端的持仓图生成,反正......
  • C++默认参数实现原理分析
    简介定义C++默认参数指的是当函数调用中省略了实参时自动使用的一个值。写法如下:voidfoo(inta=1){}voidfoo(inta,intb=1,intc=1){}默认参数有两个规则,规则一:从第一个出现默认参数的参数开始,后面的参数必须也指定默认参数voidfoo(inta=1,intb,intc=1......
  • Code-C++-字符串分割
    Code-C++-字符串分割转自【C++中string如何实现字符串分割函数split()——4种方法-CSDNApp】http://t.csdnimg.cn/8iWb7stringstreamgetline()stringfind()substr()ccharstrtok()strtok_r()regex_token_iterator<>getline()voidStringsplit(stringstr,const......
  • c++线程专题
    逐步更新中~~~,参考书籍《C++并发编程实战(第2版)》,不照搬书,只写理解感悟。引入头文件#include<thread>线程启动std::threadt(my_func);若需等待线程执行完毕,才继续之后的代码,用joinif(t.joinable()){t.join();}若不等待,可以分离出去(分离出去的线程被称为守护......
  • C++ Primer学习笔记——第十二章
    第十二章动态内存前言在此之前,我们使用的程序中对象都有着严格定义的生存期:全局对象,在程序启动时分配,在程序结束是销毁。局部自动对象,当进入定义所在程序时创建,在离开块时销毁。局部static对象,在第一次使用前分配,在程序结束时销毁。显然这存在限制,为此C++支持动态分配对......