how to acquire the primary display IDXGIOutput in a session 0 service running under LocalSystem

182 Views Asked by At

I have a remote access app that uses IDXGIOutputDuplication to capture the primary display. To do that, I need to acquire the display IDXGIOutput:

// intermediate variables for casting
    IDXGIOutput* pDisplay_old;

    IDXGIFactory1* pFactory;
    IDXGIAdapter* pGPU;
    ID3D11Device* pD3D;
    ID3D11DeviceContext* pD3DContext;
    IDXGIOutput1* pDisplay;

    // create DXGI factory
    res = CreateDXGIFactory1(IID_PPV_ARGS(&pFactory));
    if (FAILED(res))
    {
        cerr << "CreateDXGIFactory1 " << res;
        return 1;
    }

    // get GPU adapter
    res = pFactory->EnumAdapters(0, &pGPU);
    if (FAILED(res))
    {
        cerr << "EnumAdapters " << res;
        return 1;
    }

    // create D3D11 device
    res = D3D11CreateDevice(pGPU, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &pD3D, NULL, &pD3DContext);
    if (FAILED(res))
    {
        cerr << "D3D11CreateDevice " << res;
        return 1;
    }

    // get display
    res = pGPU->EnumOutputs(0, &pDisplay_old);
    if (FAILED(res))
    {
        cerr << "EnumOutputs " << res;
        return 1;
    }
    res = pDisplay_old->QueryInterface(&pDisplay);
    if (FAILED(res))
    {
        cerr << "QueryInterface " << res;
        return 1;
    }

    // free resources
    pDisplay_old->Release();
    pGPU->Release();
    pFactory->Release();

this works fine when debugging in visual studio, but EnumOutputs() doesn't work in a session 0 process. I want to run my app as a service because it's a remote access app that must be online at all times, and it must be run under localsystem so that IDXGIOutput1::DuplicateOutput can capture secure desktops such as the sign in page and uac prompts.

clearly, based on the documentation, using IDXGIOutputDuplication in a session 0 service is a supported use case, and I wish to know how to acquire the primary display IDXGIOutput in a session 0 service running under LocalSystem, so that I can call DuplicateOutput() on it as normal.

Update: I have added this code at the beginning of the snippet above:

// Assigns the specified desktop to the calling thread
    HDESK currentInputDesktop = OpenInputDesktop(0, false, GENERIC_ALL);
    if (currentInputDesktop == NULL)
    {
        cerr << "OpenInputDesktop " << GetLastError();
        return 1;
    }

    res = SetThreadDesktop(currentInputDesktop);
    if (res == 0)
    {
        cerr << "SetThreadDesktop " << GetLastError();
        return 1;
    }

    CloseDesktop(currentInputDesktop);

running my code as localsystem using psexec -s -h led to OpenInputDesktop erroring, with the code from GetLastError() being simply "1", which I don't know how to parse

0

There are 0 best solutions below