该代码来源于codeproject,经过测试发现,在屏幕处于旋转的情况下捕获的图像是黑色的。暂时没有找到原因。

代码开箱即用,

#define WIN32_LEAN_AND_MEAN    

#include <windows.h>
#include <shlobj.h>
#include <shellapi.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#include <memory>
#include <algorithm>
#include <string> #pragma comment(lib, "D3D11.lib") template <typename T>
class CComPtrCustom
{
public: CComPtrCustom(T *aPtrElement)
:element(aPtrElement)
{
} CComPtrCustom()
:element(nullptr)
{
} virtual ~CComPtrCustom()
{
Release();
} T* Detach()
{
auto lOutPtr = element; element = nullptr; return lOutPtr;
} T* detach()
{
return Detach();
} void Release()
{
if (element == nullptr)
return; auto k = element->Release(); element = nullptr;
} CComPtrCustom& operator = (T *pElement)
{
Release(); if (pElement == nullptr)
return *this; auto k = pElement->AddRef(); element = pElement; return *this;
} void Swap(CComPtrCustom& other)
{
T* pTemp = element;
element = other.element;
other.element = pTemp;
} T* operator->()
{
return element;
} operator T*()
{
return element;
} operator T*() const
{
return element;
} T* get()
{
return element;
} T* get() const
{
return element;
} T** operator &()
{
return &element;
} bool operator !()const
{
return element == nullptr;
} operator bool()const
{
return element != nullptr;
} bool operator == (const T *pElement)const
{
return element == pElement;
} CComPtrCustom(const CComPtrCustom& aCComPtrCustom)
{
if (aCComPtrCustom.operator!())
{
element = nullptr; return;
} element = aCComPtrCustom; auto h = element->AddRef(); h++;
} CComPtrCustom& operator = (const CComPtrCustom& aCComPtrCustom)
{
Release(); element = aCComPtrCustom; auto k = element->AddRef(); return *this;
} _Check_return_ HRESULT CopyTo(T** ppT) throw()
{
if (ppT == NULL)
return E_POINTER; *ppT = element; if (element)
element->AddRef(); return S_OK;
} HRESULT CoCreateInstance(const CLSID aCLSID)
{
T* lPtrTemp; auto lresult = ::CoCreateInstance(aCLSID, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&lPtrTemp)); if (SUCCEEDED(lresult))
{
if (lPtrTemp != nullptr)
{
Release(); element = lPtrTemp;
} } return lresult;
} protected: T* element;
}; // Driver types supported
D3D_DRIVER_TYPE gDriverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE
};
UINT gNumDriverTypes = ARRAYSIZE(gDriverTypes); // Feature levels supported
D3D_FEATURE_LEVEL gFeatureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_1
}; UINT gNumFeatureLevels = ARRAYSIZE(gFeatureLevels); int main()
{
CComPtrCustom<ID3D11Device> lDevice;
CComPtrCustom<ID3D11DeviceContext> lImmediateContext;
CComPtrCustom<IDXGIOutputDuplication> lDeskDupl;
CComPtrCustom<ID3D11Texture2D> lAcquiredDesktopImage;
CComPtrCustom<ID3D11Texture2D> lGDIImage;
CComPtrCustom<ID3D11Texture2D> lDestImage;
DXGI_OUTPUT_DESC lOutputDesc;
DXGI_OUTDUPL_DESC lOutputDuplDesc; int lresult(-1); do
{ D3D_FEATURE_LEVEL lFeatureLevel; HRESULT hr(E_FAIL); // Create device
for (UINT DriverTypeIndex = 0; DriverTypeIndex < gNumDriverTypes; ++DriverTypeIndex)
{
hr = D3D11CreateDevice(
nullptr,
gDriverTypes[DriverTypeIndex],
nullptr,
0,
gFeatureLevels,
gNumFeatureLevels,
D3D11_SDK_VERSION,
&lDevice,
&lFeatureLevel,
&lImmediateContext); if (SUCCEEDED(hr))
{
// Device creation success, no need to loop anymore
break;
} lDevice.Release(); lImmediateContext.Release();
} if (FAILED(hr))
break; Sleep(100); if (lDevice == nullptr)
break; // Get DXGI device
CComPtrCustom<IDXGIDevice> lDxgiDevice; hr = lDevice->QueryInterface(IID_PPV_ARGS(&lDxgiDevice)); if (FAILED(hr))
break; // Get DXGI adapter
CComPtrCustom<IDXGIAdapter> lDxgiAdapter;
hr = lDxgiDevice->GetParent(
__uuidof(IDXGIAdapter),
reinterpret_cast<void**>(&lDxgiAdapter)); if (FAILED(hr))
break; lDxgiDevice.Release(); UINT Output = 0; // Get output
CComPtrCustom<IDXGIOutput> lDxgiOutput;
hr = lDxgiAdapter->EnumOutputs(
Output,
&lDxgiOutput); if (FAILED(hr))
break; lDxgiAdapter.Release(); hr = lDxgiOutput->GetDesc(
&lOutputDesc); if (FAILED(hr))
break; // QI for Output 1
CComPtrCustom<IDXGIOutput1> lDxgiOutput1; hr = lDxgiOutput->QueryInterface(IID_PPV_ARGS(&lDxgiOutput1)); if (FAILED(hr))
break; lDxgiOutput.Release(); // Create desktop duplication
hr = lDxgiOutput1->DuplicateOutput(
lDevice,
&lDeskDupl); if (FAILED(hr))
break; lDxgiOutput1.Release(); // Create GUI drawing texture
lDeskDupl->GetDesc(&lOutputDuplDesc); D3D11_TEXTURE2D_DESC desc; desc.Width = lOutputDuplDesc.ModeDesc.Width; desc.Height = lOutputDuplDesc.ModeDesc.Height; desc.Format = lOutputDuplDesc.ModeDesc.Format; desc.ArraySize = 1; desc.BindFlags = D3D11_BIND_FLAG::D3D11_BIND_RENDER_TARGET; desc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.MipLevels = 1; desc.CPUAccessFlags = 0; desc.Usage = D3D11_USAGE_DEFAULT; hr = lDevice->CreateTexture2D(&desc, NULL, &lGDIImage); if (FAILED(hr))
break; if (lGDIImage == nullptr)
break; // Create CPU access texture desc.Width = lOutputDuplDesc.ModeDesc.Width; desc.Height = lOutputDuplDesc.ModeDesc.Height; desc.Format = lOutputDuplDesc.ModeDesc.Format; desc.ArraySize = 1; desc.BindFlags = 0; desc.MiscFlags = 0; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.MipLevels = 1; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
desc.Usage = D3D11_USAGE_STAGING; hr = lDevice->CreateTexture2D(&desc, NULL, &lDestImage); if (FAILED(hr))
break; if (lDestImage == nullptr)
break; CComPtrCustom<IDXGIResource> lDesktopResource;
DXGI_OUTDUPL_FRAME_INFO lFrameInfo; int lTryCount = 4; do
{ Sleep(100); // Get new frame
hr = lDeskDupl->AcquireNextFrame(
250,
&lFrameInfo,
&lDesktopResource); if (SUCCEEDED(hr))
break; if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
continue;
}
else if (FAILED(hr))
break; } while (--lTryCount > 0); if (FAILED(hr))
break; // QI for ID3D11Texture2D hr = lDesktopResource->QueryInterface(IID_PPV_ARGS(&lAcquiredDesktopImage)); if (FAILED(hr))
break; lDesktopResource.Release(); if (lAcquiredDesktopImage == nullptr)
break; // Copy image into GDI drawing texture lImmediateContext->CopyResource(lGDIImage, lAcquiredDesktopImage); // Draw cursor image into GDI drawing texture CComPtrCustom<IDXGISurface1> lIDXGISurface1; hr = lGDIImage->QueryInterface(IID_PPV_ARGS(&lIDXGISurface1)); if (FAILED(hr))
break; CURSORINFO lCursorInfo = { 0 }; lCursorInfo.cbSize = sizeof(lCursorInfo); auto lBoolres = GetCursorInfo(&lCursorInfo); if (lBoolres == TRUE)
{
if (lCursorInfo.flags == CURSOR_SHOWING)
{
auto lCursorPosition = lCursorInfo.ptScreenPos; auto lCursorSize = lCursorInfo.cbSize; HDC lHDC; lIDXGISurface1->GetDC(FALSE, &lHDC); DrawIconEx(
lHDC,
lCursorPosition.x,
lCursorPosition.y,
lCursorInfo.hCursor,
0,
0,
0,
0,
DI_NORMAL | DI_DEFAULTSIZE); lIDXGISurface1->ReleaseDC(nullptr);
} } // Copy image into CPU access texture lImmediateContext->CopyResource(lDestImage, lGDIImage); // Copy from CPU access texture to bitmap buffer D3D11_MAPPED_SUBRESOURCE resource;
UINT subresource = D3D11CalcSubresource(0, 0, 0);
lImmediateContext->Map(lDestImage, subresource, D3D11_MAP_READ_WRITE, 0, &resource); BITMAPINFO lBmpInfo; // BMP 32 bpp ZeroMemory(&lBmpInfo, sizeof(BITMAPINFO)); lBmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); lBmpInfo.bmiHeader.biBitCount = 32; lBmpInfo.bmiHeader.biCompression = BI_RGB; lBmpInfo.bmiHeader.biWidth = lOutputDuplDesc.ModeDesc.Width; lBmpInfo.bmiHeader.biHeight = lOutputDuplDesc.ModeDesc.Height; lBmpInfo.bmiHeader.biPlanes = 1; lBmpInfo.bmiHeader.biSizeImage = lOutputDuplDesc.ModeDesc.Width
* lOutputDuplDesc.ModeDesc.Height * 4; std::unique_ptr<BYTE> pBuf(new BYTE[lBmpInfo.bmiHeader.biSizeImage]); UINT lBmpRowPitch = lOutputDuplDesc.ModeDesc.Width * 4; BYTE* sptr = reinterpret_cast<BYTE*>(resource.pData);
BYTE* dptr = pBuf.get() + lBmpInfo.bmiHeader.biSizeImage - lBmpRowPitch; UINT lRowPitch = std::min<UINT>(lBmpRowPitch, resource.RowPitch); for (size_t h = 0; h < lOutputDuplDesc.ModeDesc.Height; ++h)
{ memcpy_s(dptr, lBmpRowPitch, sptr, lRowPitch);
sptr += resource.RowPitch;
dptr -= lBmpRowPitch;
} // Save bitmap buffer into the file ScreenShot.bmp WCHAR lMyDocPath[MAX_PATH]; hr = SHGetFolderPath(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, lMyDocPath); if (FAILED(hr))
break; std::wstring lFilePath = std::wstring(lMyDocPath) + L"\\ScreenShot.bmp"; FILE* lfile = nullptr; auto lerr = _wfopen_s(&lfile, lFilePath.c_str(), L"wb"); if (lerr != 0)
break; if (lfile != nullptr)
{ BITMAPFILEHEADER bmpFileHeader; bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lBmpInfo.bmiHeader.biSizeImage;
bmpFileHeader.bfType = 'MB';
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, lfile);
fwrite(&lBmpInfo.bmiHeader, sizeof(BITMAPINFOHEADER), 1, lfile);
fwrite(pBuf.get(), lBmpInfo.bmiHeader.biSizeImage, 1, lfile); fclose(lfile); ShellExecute(0, 0, lFilePath.c_str(), 0, 0, SW_SHOW); lresult = 0; } } while (false); return lresult;
}

如果需要录屏,可以参考另一个文章:screen-recorder

win32 - 使用Desktop Duplication API复制桌面图像的更多相关文章

  1. Win32++:可替代MFC的Windows桌面应用开发框架

    写在前面 有过Win32编程经验的朋友都知道,使用Windows提供的API开发桌面应用是相当繁琐的,创建一个功能简单能接收并处理消息的窗口至少也得几百行代码.创建一个可视化的窗口一般要以下几个步骤: ...

  2. C#使用Windows API实现桌面上的遮罩层(鼠标穿透)

    C#使用Windows API实现桌面上的遮罩层(鼠标穿透) C#实现实现桌面上的遮罩层(鼠标穿透)主要通过一下几个API函数来实现:GetWindowLong,SetWindowLong,SetLa ...

  3. [转]OpenGL 使用 PBO 高速复制屏幕图像到内存或者纹理中

    如果你想给游戏做个截图功能,或者想把屏幕图像弄成一个纹理,你就非常需要 PBO 了 通常情况下,你想把屏幕图像的像素数据读到内存需要用 glReadPixels 然后 pixels 参数传进去一块内存 ...

  4. VC Windows API获得桌面所有窗口句柄的方法

    VC Windows API应用之GetDesktopWindow ——获得桌面所有窗口句柄的方法 Windows API Windows 这个多作业系统除了协调应用程序的执行.分配内存.管理资源…之 ...

  5. OpenGL 使用 PBO 高速复制屏幕图像到内存或者纹理中

    如果你想给游戏做个截图功能,或者想把屏幕图像弄成一个纹理,你就非常需要 PBO 了 通常情况下,你想把屏幕图像的像素数据读到内存需要用 glReadPixels 然后 pixels 参数传进去一块内存 ...

  6. 利用API设置桌面背景

    实现效果: 知识运用: API函数SystemParametersInfo 实现代码: [DllImport("user32.dll", EntryPoint = "Sy ...

  7. 使用Desktop App Converter打包桌面应用程序

    打包具有安装程序 (.msi) 的应用程序 DesktopAppConverter.exe -Installer C:\Installer\MyAppSetup.msi -Destination C: ...

  8. 【Ubuntu Desktop】安装主流桌面

    ubuntu的桌面环境实在多,在这里选了几款主流的桌面环境,大家可以按需安装使用. 1.GNOME 2 Classic 经典老界面 gnome2   after reboot,choose GNOME ...

  9. 通过微信公众号API复制公众号自定义菜单同时增加子菜单方法

    主要的原因是再不破坏公众号以前的菜单的基础上增加自定义菜单,主要步骤如下: 1.通过微信提供的微信公众平台接口调试工具获取公众号的所有自定义菜单 网址:https://mp.weixin.qq.com ...

  10. Windows api实现桌面任务栏隐藏\显示

    //隐藏任务栏 HWND hWnd = ::FindWindow(TEXT("Shell_traywnd"),TEXT("")); ::SetWindowPos ...

随机推荐

  1. [转帖]oracle rac后台进程和LMS说明

    本文摘抄录oracle官方文档,oracle rac使用的后台进程,用以备忘,记录之. About Oracle RAC Background Processes The GCS and GES pr ...

  2. [转帖]tidb4.0.4使用tiup扩容TiKV 节点

    https://blog.csdn.net/mchdba/article/details/108896766 环境:centos7.tidb4.0.4.tiup-v1.0.8 添加两个tikv节点   ...

  3. [转帖]如何通过JMeter测试金仓数据库KingbaseES并搭建环境

    1.安装JMeter Apache JMeter是Apache组织开发的基于Java的压力测试工具,主要用于对软件的压力测试,它最初被设计用于Web应用测试,但后来扩展到其它测试领域.它可测试静态.动 ...

  4. kafka的学习之二_kafka的压测与GUI管理

    kafka的学习之二_kafka的压测与GUI管理 第一部分创建topic cd /root/kafka_2.13-3.5.0 bin/kafka-topics.sh --create --boots ...

  5. rust入坑指南之ownership

    作者:京东零售 王梦津 I. 前言 Rust,不少程序员的白月光,这里我们简单罗列一些大牛的评价. Linus Torvalds:Linux内核的创始人,对Rust的评价是:"Rust的主要 ...

  6. devDependencies和dependencies有何区别

    01==>devDependencies用于本地环境开发时候所需要的依赖包. 上线后就不在需要了: npm i webpack-cli -D --save-dev等价为-D 意思是安装到开发环境 ...

  7. slices in Go 1.21

    Go 1.21中新增的 slices包中提供了很多与切片相关的函数,适用于任意类型的切片. 本文内容来自官方文档 BinarySearch 函数签名如下: func BinarySearch[S ~[ ...

  8. 在Protocol Buffers中导入当前目录中的.proto文件

    在protobuf中导入当前目录中的.proto文件时,可以使用相对路径.相对路径是相对于当前.proto文件所在的目录来引用其他.proto文件. 假设有以下目录结构: my_project/ |- ...

  9. C#对象属性浅拷贝和深拷贝

    对象属性和字段拷贝的几种方式 微软提供了浅拷贝 对于值类型,修改拷贝的值不会影响源对象 对于引用类型,修改拷贝后的值会影响源对象,但string特殊,它会拷贝一个副本,互相不会影响 自己实现深拷贝,我 ...

  10. C/C++ 进程线程操作技术

    手动创建单进程: 下面通过一个实例来分别演示进程的创建函数. #include <windows.h> #include <stdio.h> BOOL WinExec(char ...