How to screenshot an active non-focused window using WinRT
See the question and my original answer on StackOverflowYou don't need Direct2D (Win2D is a wrapper over Direct2D) to get the bytes from a surface. You can get the bytes using standard DirectX API, then write them to a file using the Bitmap API you want.
Here is an example using WinRT's SoftwareBitmap and BitmapEncoder, but you can use GDI+ (System.Drawing.Bitmap, WIC, etc.) to save a file.
Starting from the BasicCapture.cs file, modify it like this:
public class BasicCapture : IDisposable
{
...
private Texture2D cpuTexture; // add this
private bool saved = false; // add this
public BasicCapture(IDirect3DDevice d, GraphicsCaptureItem i)
{
...
cpuTexture = CreateTexture2D(item.Size.Width, item.Size.Height); // add this
}
public void Dispose()
{
session?.Dispose();
framePool?.Dispose();
swapChain?.Dispose();
d3dDevice?.Dispose();
cpuTexture?.Dispose(); // add this
}
private Texture2D CreateTexture2D(int width, int height)
{
// create add texture2D 2D accessible by the CPU
var desc = new Texture2DDescription()
{
Width = width,
Height = height,
CpuAccessFlags = CpuAccessFlags.Read,
Usage = ResourceUsage.Staging,
Format = Format.B8G8R8A8_UNorm,
ArraySize = 1,
MipLevels = 1,
SampleDescription = new SampleDescription(1, 0),
};
return new Texture2D(d3dDevice, desc);
}
private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
{
using (var frame = sender.TryGetNextFrame())
{
...
using (var backBuffer = swapChain.GetBackBuffer<SharpDX.Direct3D11.Texture2D>(0))
using (var bitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface))
{
d3dDevice.ImmediateContext.CopyResource(bitmap, backBuffer);
// add this to copy the DirectX resource into the CPU-readable texture2D
d3dDevice.ImmediateContext.CopyResource(bitmap, cpuTexture);
// now, this is just an example that only saves the first frame
// but you could also use
// d3dDevice.ImmediateContext.MapSubresource(cpuTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None, out var stream); and d3dDevice.ImmediateContext.UnMapSubresource
// to get the bytes (out from the returned stream)
if (!_saved)
{
_saved = true;
Task.Run(async () =>
{
// get IDirect3DSurface from texture (from WPF sample's helper code)
var surf = Direct3D11Helper.CreateDirect3DSurfaceFromSharpDXTexture(cpuTexture);
// build a WinRT's SoftwareBitmap from this surface/texture
var softwareBitmap = await SoftwareBitmap.CreateCopyFromSurfaceAsync(surf);
using (var file = new FileStream(@"c:\temp\test.png", FileMode.Create, FileAccess.Write))
{
// create a PNG encoder
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, file.AsRandomAccessStream());
// set the bitmap to it & flush
encoder.SetSoftwareBitmap(softwareBitmap);
await encoder.FlushAsync();
}
});
}
}
}
}
}