LOST
LOST

Reputation: 3250

Desktop Duplication API returns blank image inside Windows Sandbox

I am fiddling with recording the screen of Windows Sandbox based on https://github.com/mika-f/dotnet-window-capture :

var hr = _duplication.TryAcquireNextFrame(100, out _, out var desktopResourceOut);
if (hr.Failure)
    return null;

using var desktopTexture = desktopResourceOut.QueryInterface<Texture2D>();
var texture2dDescription = new Texture2DDescription {
    ArraySize = 1,
    BindFlags = BindFlags.ShaderResource | BindFlags.RenderTarget,
    CpuAccessFlags = CpuAccessFlags.None,
    Format = Format.B8G8R8A8_UNorm,
    Height = desktopTexture.Description.Height,
    MipLevels = 1,
    SampleDescription = new SampleDescription(1, 0),
    Usage = ResourceUsage.Default,
    Width = desktopTexture.Description.Width
};
var texture2d = new Texture2D(device, texture2dDescription);
device.ImmediateContext.CopyResource(desktopTexture, texture2d);

// release resources
desktopResourceOut.Dispose();
_duplication.ReleaseFrame();

return texture2d;

Problem is from the start the code works on my own machine and also via RDP. But inside the Windows Sandbox all I get is a blank window.

Now I searched around, and found a few "deficiencies" in the above sample, namely:

  1. All failures of TryAcquireNextFrame are ignored silently.
  2. OutputDuplicateFrameInformation is ignored. Specifically, it never checks LastPresentTime which must be checked according to this SO thread (although that contradicts When AcquireNextFrame returns successfully, the calling application can access the desktop image that AcquireNextFrame returns in the variable at ppDesktopResource from MSDN).

So after fiddling with error handling and OutputDuplicateFrameInformation all I am getting from Windows Sandbox is:

  1. As long as I move mouse around, TryAcquireNextFrame mostly succeeds, however OutputDuplicateFrameInformation.LastPresentTime is always 0.
  2. The only error code I get in simple tests is 0x887A0027 aka timeout, which just indicates the need to wait more as there was no picture update.
  3. If I ignore LastPresentTime == 0, the texture2d comes out black.
  4. I also tried to copy some Texture2DDescription parameters directly from desktopTexture in attempt to avoid potential silent failure of CopyResource as suggested in this SO thread, but that had no visible effect.
  5. I enumerated Adapters and their associated Outputs, and there's only one Output in the system.

The final modification that still exhibits the blank texture2d behavior is

var hr = _duplication.TryAcquireNextFrame(100, out var frameInfo, out var desktopResourceOut);
if (hr.Failure) {
    if (hr.Code == unchecked((int)0x887A0027)) return null; // no changes
    hr.CheckError();
    return null;
}

try {
    //if (frameInfo.LastPresentTime == 0) {
    //    return null; // in Sandbox all frames go here
    //}

    using var desktopTexture = desktopResourceOut.QueryInterface<Texture2D>();
    var texture2dDescription = new Texture2DDescription {
        ArraySize = desktopTexture.Description.ArraySize,
        BindFlags = BindFlags.ShaderResource | BindFlags.RenderTarget,
        CpuAccessFlags = CpuAccessFlags.None,
        Format = desktopTexture.Description.Format,
        Height = desktopTexture.Description.Height,
        MipLevels = 1,
        SampleDescription = new SampleDescription(1, 0),
        Usage = ResourceUsage.Default,
        Width = desktopTexture.Description.Width
    };
    var texture2d = new Texture2D(device, texture2dDescription);
    device.ImmediateContext.CopyResource(desktopTexture, texture2d);

    return texture2d;
} finally {
    // release resources
    desktopResourceOut.Dispose();
    _duplication.ReleaseFrame();
}

P.S. As alternative I also tried WinRT's IGraphicsCaptureItemInterop::CreateForMonitor(hmon, new Guid("79C3F95B-31F7-4EC2-A464-632EF5D30760")) but this one fails with 0x80040154 REGDB_E_CLASSNOTREG.

Upvotes: 0

Views: 428

Answers (0)

Related Questions