DX后臺截圖C++實(shí)現(xiàn)代碼
文章僅發(fā)布于https://www.cnblogs.com/Icys/p/DXGI.html和知乎上。
傳統(tǒng)的GDI API (BitBlt)雖然可以完美的完成后臺截圖的任務(wù),但是歸根結(jié)底效率還是太低。
直接使用DXGI方法截圖只能完成前臺窗口的截圖,而DX HOOK的截圖方法平添風(fēng)險(xiǎn),以及很多場景不現(xiàn)實(shí)。
本文講介紹使用 DwmGetDxSharedSurface 函數(shù),優(yōu)雅的完成后臺截圖的工作。
API介紹
函數(shù)定義
BOOL WINAPI DwmGetDxSharedSurface (
HWND hwnd,
HANDLE* phSurface,
LUID* pAdapterLuid,
ULONG* pFmtWindow,
ULONG* pPresentFlags,
ULONGLONG* pWin32kUpdateId
)
\(DwmGetDxSharedSurface\)來自于user32.dll(很離譜是吧,DwmApi不在DwmApi.dll里)。由于是ms沒有公開的API,需要使用動態(tài)方法加載。
調(diào)用函數(shù)方法
//動態(tài)載入該函數(shù)
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函數(shù) 在 user32.dll 中,真是離譜
if (DwmGetDxSharedSurface == NULL)
{
std::cout << "GetProcAddress failed" << std::endl;
return 0;
}
std::cout << DwmGetDxSharedSurface << std::endl;
參數(shù)含義
- hwnd 被截圖窗口的句柄
- phSurface 被截圖窗口的共享畫面的句柄(應(yīng)該是這么翻譯吧)
- 其他,暫時(shí)還沒了解。
API調(diào)用
問題
顯然這個(gè)API不能一步到位獲得到BMP或者其他類型的圖像數(shù)據(jù)。和BitBlt一樣,這個(gè)API只是拿到了對應(yīng)畫面的副本(?,不清楚這樣描述是否準(zhǔn)確)。參照唯一有官方信息的API\(DwmDxGetWindowSharedSurface\),得到的是DX的一個(gè)對象,那就應(yīng)該從DX下手。
初始化DX
這里講個(gè)遇到的坑,DX設(shè)備的初始化不能在dllmain里進(jìn)行,否則會失敗。
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;
}
將數(shù)據(jù)載入
HRESULT hr = S_OK;
ID3D11Texture2D* sharedSurface = NULL;
hr = device->OpenSharedResource(phSurface, __uuidof(ID3D11Texture2D), (void**)&sharedSurface);//打開對應(yīng)資源
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);
這里我們其實(shí)就已經(jīng)拿到了對應(yīng)的圖片資源
數(shù)據(jù)轉(zhuǎn)化
根據(jù)DX設(shè)備填入的D3D11_CREATE_DEVICE_BGRA_SUPPORT??梢灾?/p>
typedef struct D3D11_MAPPED_SUBRESOURCE {
void *pData;
UINT RowPitch;
UINT DepthPitch;
} D3D11_MAPPED_SUBRESOURCE;
其中的pData應(yīng)該是一段對應(yīng)像素排列位BGRA的位圖。RowPitch是每行數(shù)據(jù)站的字長。為了方便我采用的是用OpenCV直接讀入這段數(shù)據(jù)
cv::Mat mat(shared_desc.Height, shared_desc.Width, CV_8UC4, mappedResource.pData, mappedResource.RowPitch);
cv::imshow("mat", mat);
cv::waitKey(0);
//轉(zhuǎn)BMP寫出
std::vector<uchar> buffer;
cv::imencode(".bmp", mat, buffer);
當(dāng)然也能用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沒法正常關(guān)掉phSurface,暫時(shí)不知道什么解決或方法,或是需不需要關(guān)掉
庫的鏈接
用到了DX方面的庫,當(dāng)然要把他們的lib給鏈接上,在cpp文件中添加以下代碼文章來源:http://www.zghlxwxcb.cn/news/detail-746478.html
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
問題
這個(gè)API截取不到標(biāo)題欄。另外也可能是本人對API和DX的理解水平還不到位D2D/D3D渲染的窗口截圖是全黑的。文章來源地址http://www.zghlxwxcb.cn/news/detail-746478.html
到了這里,關(guān)于DX后臺截圖C++實(shí)現(xiàn)代碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!