desktop duplication是一组WIN8以后才有的桌面截图的协作方案。功能在我看来就是截取桌面屏幕,微软给的示例要命,代码里面老喜欢判断返回值,写个程序贼稳,一来一回代码量就多了,一点不像是示例。再加上里面有各种各样的类、方法和多线程编程。让我一开始拿来的时候是懵笔的,本想直接取里面的函数实现功能而已,发现不是特别好分开,现在终于是搞清楚大概的结构了,本文只讲结构,不讲具体怎么实现的,
其中没加了后缀的是有头文件和源文件两个文件,没加的就只有后缀那个文件。
另外VertexShader.h,PixelShader.h这两个文件虽然没有显示在解决方案里,但是后面还是要用到,里面内容古怪,大概定义了什么结构,并不重要。
一一的说明下吧!
commonType.h定义了整个工程所用的库,头文件,以及必要的对象。
后面四个模块都包括commonType.h,并各自有着功能,后面分析函数在讲有什么功能。
主函数是函数入口,里面包括了注册创建窗体,调用其他类实现功能等。
程序大体思路如下图
这个程序用的directx11的一些东西,本人不了解,所以我也说不清具体细节,值得一说的也就DDProc这个函数,
WORD WINAPI DDProc(_In_ void* Param)
{
// Classes
DISPLAYMANAGER DispMgr;
DUPLICATIONMANAGER DuplMgr;
// D3D objects
ID3D11Texture2D* SharedSurf = nullptr;
IDXGIKeyedMutex* KeyMutex = nullptr;
// Data passed in from thread creation
THREAD_DATA* TData = reinterpret_cast<THREAD_DATA*>(Param);
// Get desktop
DUPL_RETURN Ret;
HDESK CurrentDesktop = nullptr;
CurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL);
if (!CurrentDesktop)
{
// We do not have access to the desktop so request a retry
SetEvent(TData->ExpectedErrorEvent);
Ret = DUPL_RETURN_ERROR_EXPECTED;
goto Exit;
}
// Attach desktop to this thread
bool DesktopAttached = SetThreadDesktop(CurrentDesktop) != 0;
CloseDesktop(CurrentDesktop);
CurrentDesktop = nullptr;
if (!DesktopAttached)
{
// We do not have access to the desktop so request a retry
Ret = DUPL_RETURN_ERROR_EXPECTED;
goto Exit;
}
// New display manager
DispMgr.InitD3D(&TData->DxRes);
// Obtain handle to sync shared Surface
HRESULT hr = TData->DxRes.Device->OpenSharedResource(TData->TexSharedHandle, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&SharedSurf));
if (FAILED (hr))
{
Ret = ProcessFailure(TData->DxRes.Device, L"Opening shared texture failed", L"Error", hr, SystemTransitionsExpectedErrors);
goto Exit;
}
hr = SharedSurf->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&KeyMutex));
if (FAILED(hr))
{
Ret = ProcessFailure(nullptr, L"Failed to get keyed mutex interface in spawned thread", L"Error", hr);
goto Exit;
}
// Make duplication manager
Ret = DuplMgr.InitDupl(TData->DxRes.Device, TData->Output);
if (Ret != DUPL_RETURN_SUCCESS)
{
goto Exit;
}
// Get output description
DXGI_OUTPUT_DESC DesktopDesc;
RtlZeroMemory(&DesktopDesc, sizeof(DXGI_OUTPUT_DESC));
DuplMgr.GetOutputDesc(&DesktopDesc);
// Main duplication loop
bool WaitToProcessCurrentFrame = false;
FRAME_DATA CurrentData;
while ((WaitForSingleObjectEx(TData->TerminateThreadsEvent, 0, FALSE) == WAIT_TIMEOUT))
{
if (!WaitToProcessCurrentFrame)
{
// Get new frame from desktop duplication
bool TimeOut;
Ret = DuplMgr.GetFrame(&CurrentData, &TimeOut);
if (Ret != DUPL_RETURN_SUCCESS)
{
// An error occurred getting the next frame drop out of loop which
// will check if it was expected or not
break;
}
// Check for timeout
if (TimeOut)
{
// No new frame at the moment
continue;
}
}
// We have a new frame so try and process it
// Try to acquire keyed mutex in order to access shared surface
hr = KeyMutex->AcquireSync(0, 1000);
if (hr == static_cast<HRESULT>(WAIT_TIMEOUT))
{
// Can't use shared surface right now, try again later
WaitToProcessCurrentFrame = true;
continue;
}
else if (FAILED(hr))
{
// Generic unknown failure
Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error acquiring KeyMutex", L"Error", hr, SystemTransitionsExpectedErrors);
DuplMgr.DoneWithFrame();
break;
}
// We can now process the current frame
WaitToProcessCurrentFrame = false;
// Get mouse info
Ret = DuplMgr.GetMouse(TData->PtrInfo, &(CurrentData.FrameInfo), TData->OffsetX, TData->OffsetY);
if (Ret != DUPL_RETURN_SUCCESS)
{
DuplMgr.DoneWithFrame();
KeyMutex->ReleaseSync(1);
break;
}
// Process new frame
Ret = DispMgr.ProcessFrame(&CurrentData, SharedSurf, TData->OffsetX, TData->OffsetY, &DesktopDesc);
if (Ret != DUPL_RETURN_SUCCESS)
{
DuplMgr.DoneWithFrame();
KeyMutex->ReleaseSync(1);
break;
}
// Release acquired keyed mutex
hr = KeyMutex->ReleaseSync(1);
if (FAILED(hr))
{
Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error releasing the keyed mutex", L"Error", hr, SystemTransitionsExpectedErrors);
DuplMgr.DoneWithFrame();
break;
}
// Release frame back to desktop duplication
Ret = DuplMgr.DoneWithFrame();
if (Ret != DUPL_RETURN_SUCCESS)
{
break;
}
}
Exit:
if (Ret != DUPL_RETURN_SUCCESS)
{
if (Ret == DUPL_RETURN_ERROR_EXPECTED)
{
// The system is in a transition state so request the duplication be restarted
SetEvent(TData->ExpectedErrorEvent);
}
else
{
// Unexpected error so exit the application
SetEvent(TData->UnexpectedErrorEvent);
}
}
if (SharedSurf)
{
SharedSurf->Release();
SharedSurf = nullptr;
}
if (KeyMutex)
{
KeyMutex->Release();
KeyMutex = nullptr;
}
return 0;
}
大概做的事情上面图表也有,具体看下面当时做的记录
建立了两个类DISPLAYMANAGER,DUPLICATIONMANAGER
两个D3D对象 ID3D11Texture2D,IDXGIKeyedMutex
来源于进程创建的数据类型 THREAD_DATA
两个用于获得桌面的变量
DUPL_RETURN
HDESK
调用函数 OpenInputDesktop
IF判断是否获得桌面,并做出反应
将桌面挂到这个进程中(申请一个BOOL变量,调用SetThreadDesktop和CloseDesktop)
清空HDESK
IF判断成功否 没有退出
InitD3D 第一个类
获得同步共享surface (HRESULT接收来自THREAD_DATA的返回
IF判断返回值)
设置duplication manager( Ret = DuplMgr.InitDupl,并判断返回值)
获取输出描述( DXGI_OUTPUT_DESC,调用RtlZeroMemory,还有自身的GetOutputDesc)
又定义了bool变量
定义FRAME_DATA 数据
一个当循环
从desktop duplication获得新帧
错误处理
如果获得新帧错误丢弃当
如果时间耗尽就继续执行下面的代码
处理新帧( HRESULT重新赋值KeyMutex->AcquireSync)
错误处理
我们现在可以处理正确的帧(设置BOOL值)
得到鼠标光标信息(Ret = DuplMgr.GetMouse)
错误处理(释放资源)
处理帧(DispMgr.ProcessFrame)
错误处理(释放资源)
释放两种东西keyed mutex
desktop duplication
释放余下的资源。
我已经完成了把他移到MFC工程中,我是代码的搬运工,不是程序员,水平有限,勿喷。这里是演示的DEMO下载地址。
http://download.csdn.net/detail/qwerty448/9674659
MSDN上原来示例下载https://code.msdn.microsoft.com/windowsdesktop/Desktop-Duplication-Sample-da4c696a
再说一句,我使用的是用VS2015,及WIN10环境下的。WIN7我也试过,运行不起来,没有这个技术,如上面讲的,WIN8后有的技术。