天天看点

对DXGI desktop duplication sample分析,改到MFC上

desktop duplication是一组WIN8以后才有的桌面截图的协作方案。功能在我看来就是截取桌面屏幕,微软给的示例要命,代码里面老喜欢判断返回值,写个程序贼稳,一来一回代码量就多了,一点不像是示例。再加上里面有各种各样的类、方法和多线程编程。让我一开始拿来的时候是懵笔的,本想直接取里面的函数实现功能而已,发现不是特别好分开,现在终于是搞清楚大概的结构了,本文只讲结构,不讲具体怎么实现的,

对DXGI desktop duplication sample分析,改到MFC上

其中没加了后缀的是有头文件和源文件两个文件,没加的就只有后缀那个文件。

另外VertexShader.h,PixelShader.h这两个文件虽然没有显示在解决方案里,但是后面还是要用到,里面内容古怪,大概定义了什么结构,并不重要。

一一的说明下吧!

commonType.h定义了整个工程所用的库,头文件,以及必要的对象。

后面四个模块都包括commonType.h,并各自有着功能,后面分析函数在讲有什么功能。

主函数是函数入口,里面包括了注册创建窗体,调用其他类实现功能等。

程序大体思路如下图

对DXGI desktop duplication sample分析,改到MFC上

这个程序用的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后有的技术。

继续阅读