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