经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
DX后台截图C++实现代码
来源:cnblogs  作者:Icys  时间:2023/11/20 8:56:30  对本文有异议

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

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

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

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

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

API介绍

函数定义
  1. BOOL WINAPI DwmGetDxSharedSurface (
  2. HWND hwnd,
  3. HANDLE* phSurface,
  4. LUID* pAdapterLuid,
  5. ULONG* pFmtWindow,
  6. ULONG* pPresentFlags,
  7. ULONGLONG* pWin32kUpdateId
  8. )

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

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

API调用

问题

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

初始化DX

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

  1. HRESULT hr = S_OK;
  2. hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory));
  3. if (FAILED(hr))
  4. {
  5. throw "CreateDXGIFactory1 failed";
  6. return 0;
  7. }
  8. pFactory->EnumAdapters(0, &pAdapter);
  9. const D3D_FEATURE_LEVEL featureLevels[] = {
  10. D3D_FEATURE_LEVEL_11_0,
  11. D3D_FEATURE_LEVEL_10_1,
  12. D3D_FEATURE_LEVEL_10_0,
  13. D3D_FEATURE_LEVEL_9_3,
  14. D3D_FEATURE_LEVEL_9_2,
  15. D3D_FEATURE_LEVEL_9_1
  16. };
  17. D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featureLevels, 6, D3D11_SDK_VERSION, &device, NULL, NULL);
  18. if (device == NULL)
  19. {
  20. throw "D3D11CreateDevice failed";
  21. return 0;
  22. }
获取phSurface
  1. HANDLE phSurface = NULL;
  2. // 使用DWM截取屏幕
  3. DwmGetDxSharedSurface(hWnd, &phSurface, NULL, NULL, NULL, NULL);
  4. if (phSurface == NULL)
  5. {
  6. throw "Get Shared Surface Failded";
  7. return 0;
  8. }
将数据载入
  1. HRESULT hr = S_OK;
  2. ID3D11Texture2D* sharedSurface = NULL;
  3. hr = device->OpenSharedResource(phSurface, __uuidof(ID3D11Texture2D), (void**)&sharedSurface);//打开对应资源
  4. if (FAILED(hr))
  5. {
  6. throw "OpenSharedResource failed";
  7. return 0;
  8. }
  9. D3D11_TEXTURE2D_DESC shared_desc;
  10. sharedSurface->GetDesc(&shared_desc);
  11. D3D11_TEXTURE2D_DESC description;
  12. description.ArraySize = 1;
  13. description.BindFlags = 0;
  14. description.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
  15. description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
  16. description.Height = shared_desc.Height;
  17. description.MipLevels = 1;
  18. description.SampleDesc = { 1, 0 };
  19. description.Usage = D3D11_USAGE_STAGING;
  20. description.Width = shared_desc.Width;
  21. description.MiscFlags = 0;
  22. hr = S_OK;
  23. ID3D11Texture2D* texture = NULL;
  24. hr = device->CreateTexture2D(&description, NULL, &texture);
  25. if (FAILED(hr))
  26. {
  27. sharedSurface->Release();
  28. throw "CreateTexture2D failed";
  29. return 0;
  30. }
  31. ID3D11DeviceContext* context = NULL;
  32. device->GetImmediateContext(&context);
  33. context->CopyResource(texture, sharedSurface);
  34. D3D11_MAPPED_SUBRESOURCE mappedResource;
  35. context->Map(texture, 0, D3D11_MAP_READ, 0, &mappedResource);

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

数据转化

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

  1. typedef struct D3D11_MAPPED_SUBRESOURCE {
  2. void *pData;
  3. UINT RowPitch;
  4. UINT DepthPitch;
  5. } D3D11_MAPPED_SUBRESOURCE;

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

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

当然也能用MFC

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

资源释放

最后别忘记了

  1. context->Release();
  2. texture->Release();
  3. sharedSurface->Release();
  4. device->Release();
  5. pAdapter->Release();
  6. pFactory->Release();
  7. FreeLibrary(hUser32);

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

库的链接

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

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

问题

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

原文链接:https://www.cnblogs.com/Icys/p/DXGI.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号