I'm looking for a way to screenshot an active (non-focused) window using its IntPtr
, without using user32.dll
PrintWindow
as it requires running the app as admin.
I was able to screen capture using WinRT following this repo with some adjustments to make it work in dotnet 6 - https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WPF/ScreenCapture
Now all that's left is to convert the frame (Direct3D11CaptureFrame
) to an image that I can actually save.
For that purpose, I found this tutorial -
https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/screen-capture
It's for UWP, but the idea is to Convert the D3D11 surface into a Win2D object by doing -
// Convert our D3D11 surface into a Win2D object.
CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromDirect3D11Surface(
_canvasDevice,
frame.Surface);
The issue is that _canvasDevice
in that sample is an ICanvasResourceCreator
which I don't have by following the WPF screen capture repo shared above.
Iv'e tried creating a device using -
CanvasDevice.GetSharedDevice()
But using that device in the CreateFromDirect3D11Surface
method throws an exception -
The requested operation is not supported. (0x88990003)
If anyone has an idea how to fix that issue or achieve my goal using a different approach I'll appreciate any help.
Thanks!
Edit -
Added below the entire OnFrameArrived
method, where I try to convert the frame to CanvasBitmap
like its done in the UWP code sample I linked above -
private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
{
var newSize = false;
using (var frame = sender.TryGetNextFrame())
{
var canvasDevice = CanvasDevice.GetSharedDevice();
//Convert our D3D11 surface into a Win2D object.
//this method below throws the exception
_currentFrame = CanvasBitmap.CreateFromDirect3D11Surface(
canvasDevice,
frame.Surface);
if (frame.ContentSize.Width != lastSize.Width ||
frame.ContentSize.Height != lastSize.Height)
{
// The thing we have been capturing has changed size.
// We need to resize the swap chain first, then blit the pixels.
// After we do that, retire the frame and then recreate the frame pool.
newSize = true;
lastSize = frame.ContentSize;
swapChain.ResizeBuffers(
2,
lastSize.Width,
lastSize.Height,
Format.B8G8R8A8_UNorm,
SwapChainFlags.None);
}
using (var backBuffer = swapChain.GetBackBuffer<Texture2D>(0))
using (var bitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface))
{
d3dDevice.ImmediateContext.CopyResource(bitmap, backBuffer);
}
} // Retire the frame.
swapChain.Present(0, PresentFlags.None);
if (newSize)
{
framePool.Recreate(
device,
DirectXPixelFormat.B8G8R8A8UIntNormalized,
2,
lastSize);
}
}
```