Captura de pantalla

A partir de Windows 10, versión 1803, el Windows.Graphics.Capture espacio de nombres proporciona API para adquirir fotogramas de una ventana de presentación o aplicación, para crear secuencias de vídeo o instantáneas para crear experiencias colaborativas e interactivas.

Con la captura de pantalla, los desarrolladores invocan la interfaz de usuario del sistema segura para que los usuarios finales elijan la ventana de visualización o aplicación que se va a capturar y el sistema dibuja un borde de notificación amarillo alrededor del elemento capturado activamente. En el caso de varias sesiones de captura simultáneas, se dibuja un borde amarillo alrededor de cada elemento que se captura.

Nota:

Las API de captura de pantalla solo se admiten en dispositivos Windows y cascos envolventes de Windows Mixed Reality.

En este artículo se describe la captura de una sola imagen de la ventana de visualización o aplicación. Para obtener información sobre la codificación de fotogramas capturados de la pantalla a un archivo de vídeo, consulte Captura de pantalla en vídeo

Agregar la funcionalidad de captura de pantalla

Las API que se encuentran en el espacio de nombres Windows.Graphics.Capturerequieren que se declare una funcionalidad general en el manifiesto de la aplicación:

  1. Abra Package.appxmanifest en el Explorador de soluciones.
  2. Seleccione la pestaña Funcionalidades.
  3. Compruebe Captura de gráficos.

Graphics Capture

Iniciar la interfaz de usuario del sistema para iniciar la captura de pantalla

Antes de iniciar la interfaz de usuario del sistema, puede comprobar si la aplicación puede realizar capturas de pantalla actualmente. Hay varias razones por las que es posible que la aplicación no pueda usar la captura de pantalla, incluido si el dispositivo no cumple los requisitos de hardware o si la aplicación destinada a la captura de pantalla de bloques de captura. Usa el método IsSupported en la clase GraphicsCaptureSession para determinar si se admite la captura de pantalla para UWP:

// This runs when the application starts.
public void OnInitialization()
{
    if (!GraphicsCaptureSession.IsSupported())
    {
        // Hide the capture UI if screen capture is not supported.
        CaptureButton.Visibility = Visibility.Collapsed;
    }
}
Public Sub OnInitialization()
    If Not GraphicsCaptureSession.IsSupported Then
        CaptureButton.Visibility = Visibility.Collapsed
    End If
End Sub

Una vez que haya comprobado que se admite la captura de pantalla, use la claseGraphicsCapturePicker para invocar la interfaz de usuario del selector del sistema. El usuario final utiliza esta interfaz de usuario para seleccionar la ventana de visualización o aplicación de la que desea realizar capturas de pantalla. El selector devolverá unGraphicsCaptureItem que se usará para crear un GraphicsCaptureSession:

public async Task StartCaptureAsync()
{
    // The GraphicsCapturePicker follows the same pattern the
    // file pickers do.
    var picker = new GraphicsCapturePicker();
    GraphicsCaptureItem item = await picker.PickSingleItemAsync();

    // The item may be null if the user dismissed the
    // control without making a selection or hit Cancel.
    if (item != null)
    {
        // We'll define this method later in the document.
        StartCaptureInternal(item);
    }
}
Public Async Function StartCaptureAsync() As Task
    ' The GraphicsCapturePicker follows the same pattern the
    ' file pickers do.
    Dim picker As New GraphicsCapturePicker
    Dim item As GraphicsCaptureItem = Await picker.PickSingleItemAsync()

    ' The item may be null if the user dismissed the
    ' control without making a selection or hit Cancel.
    If item IsNot Nothing Then
        StartCaptureInternal(item)
    End If
End Function

Dado que se trata de código de interfaz de usuario, se debe llamar a en el subproceso de la interfaz de usuario. Si lo llama desde el code-behind de una página de su aplicación (como MainPage.xaml.cs), esto se hace automáticamente, pero, si no, puedes forzar que se ejecute en el subproceso de la interfaz de usuario con el código siguiente:

CoreWindow window = CoreApplication.MainView.CoreWindow;

await window.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
    await StartCaptureAsync();
});
Dim window As CoreWindow = CoreApplication.MainView.CoreWindow
Await window.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                                 Async Sub() Await StartCaptureAsync())

Creación de un grupo de fotogramas de captura y una sesión de captura

Con GraphicsCaptureItem, creará un Direct3D11CaptureFramePool con el dispositivo D3D, formato de píxel admitido (DXGI_FORMAT_B8G8R8A8_UNORM), número de fotogramas deseados (que puede ser cualquier número entero), y tamaño de fotograma. La propiedadContentSize de la clase GraphicsCaptureItem se puede usar como tamaño del marco:

Nota:

En los sistemas con el color HD de Windows habilitado, es posible que el formato de píxel de contenido no sea necesariamente DXGI_FORMAT_B8G8R8A8_UNORM. Para evitar el sobreclipamiento de píxeles (es decir, el contenido capturado parece lavado) al capturar contenido HDR, considere la posibilidad de usar DXGI_FORMAT_R16G16B16A16_FLOAT para cada componente de la canalización de captura, incluido Direct3D11CaptureFramePool, el destino de destino como CanvasBitmap. Depende de la necesidad, es posible que sea necesario un procesamiento adicional, como guardar en formato de contenido HDR o la asignación de tono HDR a SDR. Este artículo se centrará en la captura de contenido de SDR. Para obtener más información, consulte Uso de DirectX con pantallas de alto rango dinámico y color avanzado.

private GraphicsCaptureItem _item;
private Direct3D11CaptureFramePool _framePool;
private CanvasDevice _canvasDevice;
private GraphicsCaptureSession _session;

public void StartCaptureInternal(GraphicsCaptureItem item)
{
    _item = item;

    _framePool = Direct3D11CaptureFramePool.Create(
        _canvasDevice, // D3D device
        DirectXPixelFormat.B8G8R8A8UIntNormalized, // Pixel format
        2, // Number of frames
        _item.Size); // Size of the buffers
}
WithEvents CaptureItem As GraphicsCaptureItem
WithEvents FramePool As Direct3D11CaptureFramePool
Private _canvasDevice As CanvasDevice
Private _session As GraphicsCaptureSession

Private Sub StartCaptureInternal(item As GraphicsCaptureItem)
    CaptureItem = item

    FramePool = Direct3D11CaptureFramePool.Create(
        _canvasDevice, ' D3D device
        DirectXPixelFormat.B8G8R8A8UIntNormalized, ' Pixel format
        2, '  Number of frames
        CaptureItem.Size) ' Size of the buffers
End Sub

A continuación, obtenga una instancia de la clase GraphicsCaptureSession para direct3D11CaptureFramePool pasando porGraphicsCaptureItem al método l CreateCaptureSession:

_session = _framePool.CreateCaptureSession(_item);
_session = FramePool.CreateCaptureSession(CaptureItem)

Una vez que el usuario haya dado explícitamente su consentimiento para capturar una ventana de aplicación o mostrarla en la interfaz de usuario del sistema,GraphicsCaptureItem se puede asociar a varios objetos CaptureSession. De este modo, la aplicación puede elegir capturar el mismo elemento para varias experiencias.

Para capturar varios elementos al mismo tiempo, la aplicación debe crear una sesión de captura para cada elemento que se va a capturar, lo que requiere invocar la interfaz de usuario del selector para cada elemento que se va a capturar.

Adquisición de fotogramas de captura

Con el grupo de fotogramas y la sesión de captura creadas, llama al método StartCapture en la instancia de GraphicsCaptureSession para notificar al sistema que empiece a enviar fotogramas de captura a la aplicación:

_session.StartCapture();
_session.StartCapture()

Para adquirir estos fotogramas de captura, que son objetos Direct3D11CaptureFrame, puede usar el evento Direct3D11CaptureFramePool.FrameArrived:

_framePool.FrameArrived += (s, a) =>
{
    // The FrameArrived event fires for every frame on the thread that
    // created the Direct3D11CaptureFramePool. This means we don't have to
    // do a null-check here, as we know we're the only one  
    // dequeueing frames in our application.  

    // NOTE: Disposing the frame retires it and returns  
    // the buffer to the pool.
    using (var frame = _framePool.TryGetNextFrame())
    {
        // We'll define this method later in the document.
        ProcessFrame(frame);
    }  
};
Private Sub FramePool_FrameArrived(sender As Direct3D11CaptureFramePool, args As Object) Handles FramePool.FrameArrived
    ' The FrameArrived event is raised for every frame on the thread
    ' that created the Direct3D11CaptureFramePool. This means we
    ' don't have to do a null-check here, as we know we're the only
    ' one dequeueing frames in our application.  

    ' NOTE Disposing the frame retires it And returns  
    ' the buffer to the pool.

    Using frame = FramePool.TryGetNextFrame()
        ProcessFrame(frame)
    End Using
End Sub

Se recomienda evitar el uso del subproceso de interfaz de usuario si es posible para FrameArrived, ya que este evento se generará cada vez que haya un nuevo fotograma disponible, lo que será frecuente. Si decide escuchar FrameArrived en el subproceso de la interfaz de usuario, tenga en cuenta cuánto trabajo está realizando cada vez que se desencadena el evento.

Como alternativa, puede extraer manualmente fotogramas con el método Direct3D11CaptureFramePool.TryGetNextFrame hasta obtener todos los fotogramas que necesita.

El objetoDirect3D11CaptureFrame contiene las propiedades ContentSize, Surfacey SystemRelativeTime. SystemRelativeTime es la hora de QPC (QueryPerformanceCounter) que se puede usar para sincronizar otros elementos multimedia.

Procesar fotogramas de captura

Cada fotograma de Direct3D11CaptureFramePool se desprotegió al llamar a TryGetNextFramey se desprotegió de acuerdo con la duración del objeto Direct3D11CaptureFrame. En el caso de las aplicaciones nativas, liberar el objeto Direct3D11CaptureFrame es suficiente para volver a comprobar el marco en el grupo de fotogramas. En el caso de las aplicaciones administradas, se recomienda usar el método Direct3D11CaptureFrame.Dispose (Close en C++). Direct3D11CaptureFrame implementa la interfaz IClosable, que se proyecta como IDisposable para los autores de llamadas de C#.

Las aplicaciones no deben guardar referencias a objetos Direct3D11CaptureFrame, ni deben guardar referencias a la superficie de Direct3D subyacente después de volver a activar el marco.

Al procesar un fotograma, se recomienda que las aplicaciones tomen el bloqueo ID3D11Multithread en el mismo dispositivo asociado al objeto Direct3D11CaptureFramePool.

La superficie de Direct3D subyacente siempre será el tamaño especificado al crear (o volver a crear) Direct3D11CaptureFramePool . Si el contenido es mayor que el marco, el contenido se recorta al tamaño del marco. Si el contenido es menor que el marco, el resto del marco contiene datos indefinidos. Se recomienda que las aplicaciones copien una subcorrección mediante la propiedad ContentSize para ese Direct3D11CaptureFrame para evitar mostrar contenido indefinido.

Realice una captura de pantalla

En nuestro ejemplo, convertimos cada Direct3D11CaptureFrame en un CanvasBitmap, que forma parte de las API de Win2D.

// Convert our D3D11 surface into a Win2D object.
CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromDirect3D11Surface(
    _canvasDevice,
    frame.Surface);

Una vez que tengamos CanvasBitmap, podemos guardarlo como un archivo de imagen. En el ejemplo siguiente, lo guardamos como un archivo PNG en la carpeta Imágenes guardadas del usuario.

StorageFolder pictureFolder = KnownFolders.SavedPictures;
StorageFile file = await pictureFolder.CreateFileAsync("test.png", CreationCollisionOption.ReplaceExisting);

using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
    await canvasBitmap.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
}

React para capturar el cambio de tamaño o el dispositivo perdido

Durante el proceso de captura, las aplicaciones pueden querer cambiar aspectos de su Direct3D11CaptureFramePool. Esto incluye proporcionar un nuevo dispositivo Direct3D, cambiar el tamaño de los búferes de fotogramas o incluso cambiar el número de búferes dentro del grupo. En cada uno de estos escenarios, el método Recreateen el objetoDirect3D11CaptureFramePool es la herramienta recomendada.

Cuando se llama a Volver a crear, se descartan todos los fotogramas existentes. Esto es para evitar la entrega de fotogramas cuyas superficies de Direct3D subyacentes pertenecen a un dispositivo al que puede que la aplicación ya no tenga acceso. Por este motivo, puede ser aconsejable procesar todos los fotogramas pendientes antes de llamar a Volver a crear.

Resumen

El siguiente fragmento de código es un ejemplo completo de cómo implementar la captura de pantalla en una aplicación para UWP. En este ejemplo, tenemos dos botones en el front-end: una llama a Button_ClickAsyncy la otra llama a ScreenshotButton_ClickAsync.

Nota:

Este fragmento de código usa Win2D, una biblioteca para la representación de gráficos 2D. Consulte su documentación para obtener información sobre cómo configurarlo para el proyecto.

using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.UI.Composition;
using System;
using System.Numerics;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics;
using Windows.Graphics.Capture;
using Windows.Graphics.DirectX;
using Windows.Storage;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Hosting;

namespace ScreenCaptureTest
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        // Capture API objects.
        private SizeInt32 _lastSize;
        private GraphicsCaptureItem _item;
        private Direct3D11CaptureFramePool _framePool;
        private GraphicsCaptureSession _session;

        // Non-API related members.
        private CanvasDevice _canvasDevice;
        private CompositionGraphicsDevice _compositionGraphicsDevice;
        private Compositor _compositor;
        private CompositionDrawingSurface _surface;
        private CanvasBitmap _currentFrame;
        private string _screenshotFilename = "test.png";

        public MainPage()
        {
            this.InitializeComponent();
            Setup();
        }

        private void Setup()
        {
            _canvasDevice = new CanvasDevice();

            _compositionGraphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(
                Window.Current.Compositor,
                _canvasDevice);

            _compositor = Window.Current.Compositor;

            _surface = _compositionGraphicsDevice.CreateDrawingSurface(
                new Size(400, 400),
                DirectXPixelFormat.B8G8R8A8UIntNormalized,
                DirectXAlphaMode.Premultiplied);    // This is the only value that currently works with
                                                    // the composition APIs.

            var visual = _compositor.CreateSpriteVisual();
            visual.RelativeSizeAdjustment = Vector2.One;
            var brush = _compositor.CreateSurfaceBrush(_surface);
            brush.HorizontalAlignmentRatio = 0.5f;
            brush.VerticalAlignmentRatio = 0.5f;
            brush.Stretch = CompositionStretch.Uniform;
            visual.Brush = brush;
            ElementCompositionPreview.SetElementChildVisual(this, visual);
        }

        public async Task StartCaptureAsync()
        {
            // The GraphicsCapturePicker follows the same pattern the
            // file pickers do.
            var picker = new GraphicsCapturePicker();
            GraphicsCaptureItem item = await picker.PickSingleItemAsync();

            // The item may be null if the user dismissed the
            // control without making a selection or hit Cancel.
            if (item != null)
            {
                StartCaptureInternal(item);
            }
        }

        private void StartCaptureInternal(GraphicsCaptureItem item)
        {
            // Stop the previous capture if we had one.
            StopCapture();

            _item = item;
            _lastSize = _item.Size;

            _framePool = Direct3D11CaptureFramePool.Create(
               _canvasDevice, // D3D device
               DirectXPixelFormat.B8G8R8A8UIntNormalized, // Pixel format
               2, // Number of frames
               _item.Size); // Size of the buffers

            _framePool.FrameArrived += (s, a) =>
            {
                // The FrameArrived event is raised for every frame on the thread
                // that created the Direct3D11CaptureFramePool. This means we
                // don't have to do a null-check here, as we know we're the only
                // one dequeueing frames in our application.  

                // NOTE: Disposing the frame retires it and returns  
                // the buffer to the pool.

                using (var frame = _framePool.TryGetNextFrame())
                {
                    ProcessFrame(frame);
                }
            };

            _item.Closed += (s, a) =>
            {
                StopCapture();
            };

            _session = _framePool.CreateCaptureSession(_item);
            _session.StartCapture();
        }

        public void StopCapture()
        {
            _session?.Dispose();
            _framePool?.Dispose();
            _item = null;
            _session = null;
            _framePool = null;
        }

        private void ProcessFrame(Direct3D11CaptureFrame frame)
        {
            // Resize and device-lost leverage the same function on the
            // Direct3D11CaptureFramePool. Refactoring it this way avoids
            // throwing in the catch block below (device creation could always
            // fail) along with ensuring that resize completes successfully and
            // isn’t vulnerable to device-lost.
            bool needsReset = false;
            bool recreateDevice = false;

            if ((frame.ContentSize.Width != _lastSize.Width) ||
                (frame.ContentSize.Height != _lastSize.Height))
            {
                needsReset = true;
                _lastSize = frame.ContentSize;
            }

            try
            {
                // Take the D3D11 surface and draw it into a  
                // Composition surface.

                // Convert our D3D11 surface into a Win2D object.
                CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromDirect3D11Surface(
                    _canvasDevice,
                    frame.Surface);

                _currentFrame = canvasBitmap;

                // Helper that handles the drawing for us.
                FillSurfaceWithBitmap(canvasBitmap);
            }

            // This is the device-lost convention for Win2D.
            catch (Exception e) when (_canvasDevice.IsDeviceLost(e.HResult))
            {
                // We lost our graphics device. Recreate it and reset
                // our Direct3D11CaptureFramePool.  
                needsReset = true;
                recreateDevice = true;
            }

            if (needsReset)
            {
                ResetFramePool(frame.ContentSize, recreateDevice);
            }
        }

        private void FillSurfaceWithBitmap(CanvasBitmap canvasBitmap)
        {
            CanvasComposition.Resize(_surface, canvasBitmap.Size);

            using (var session = CanvasComposition.CreateDrawingSession(_surface))
            {
                session.Clear(Colors.Transparent);
                session.DrawImage(canvasBitmap);
            }
        }

        private void ResetFramePool(SizeInt32 size, bool recreateDevice)
        {
            do
            {
                try
                {
                    if (recreateDevice)
                    {
                        _canvasDevice = new CanvasDevice();
                    }

                    _framePool.Recreate(
                        _canvasDevice,
                        DirectXPixelFormat.B8G8R8A8UIntNormalized,
                        2,
                        size);
                }
                // This is the device-lost convention for Win2D.
                catch (Exception e) when (_canvasDevice.IsDeviceLost(e.HResult))
                {
                    _canvasDevice = null;
                    recreateDevice = true;
                }
            } while (_canvasDevice == null);
        }

        private async void Button_ClickAsync(object sender, RoutedEventArgs e)
        {
            await StartCaptureAsync();
        }

        private async void ScreenshotButton_ClickAsync(object sender, RoutedEventArgs e)
        {
            await SaveImageAsync(_screenshotFilename, _currentFrame);
        }

        private async Task SaveImageAsync(string filename, CanvasBitmap frame)
        {
            StorageFolder pictureFolder = KnownFolders.SavedPictures;

            StorageFile file = await pictureFolder.CreateFileAsync(
                filename,
                CreationCollisionOption.ReplaceExisting);

            using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
            {
                await frame.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
            }
        }
    }
}
Imports System.Numerics
Imports Microsoft.Graphics.Canvas
Imports Microsoft.Graphics.Canvas.UI.Composition
Imports Windows.Graphics
Imports Windows.Graphics.Capture
Imports Windows.Graphics.DirectX
Imports Windows.UI
Imports Windows.UI.Composition
Imports Windows.UI.Xaml.Hosting

Partial Public NotInheritable Class MainPage
    Inherits Page

    ' Capture API objects.
    WithEvents CaptureItem As GraphicsCaptureItem
    WithEvents FramePool As Direct3D11CaptureFramePool

    Private _lastSize As SizeInt32
    Private _session As GraphicsCaptureSession

    ' Non-API related members.
    Private _canvasDevice As CanvasDevice
    Private _compositionGraphicsDevice As CompositionGraphicsDevice
    Private _compositor As Compositor
    Private _surface As CompositionDrawingSurface

    Sub New()
        InitializeComponent()
        Setup()
    End Sub

    Private Sub Setup()
        _canvasDevice = New CanvasDevice()
        _compositionGraphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(Window.Current.Compositor, _canvasDevice)
        _compositor = Window.Current.Compositor
        _surface = _compositionGraphicsDevice.CreateDrawingSurface(
            New Size(400, 400), DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied)
        Dim visual = _compositor.CreateSpriteVisual()
        visual.RelativeSizeAdjustment = Vector2.One
        Dim brush = _compositor.CreateSurfaceBrush(_surface)
        brush.HorizontalAlignmentRatio = 0.5F
        brush.VerticalAlignmentRatio = 0.5F
        brush.Stretch = CompositionStretch.Uniform
        visual.Brush = brush
        ElementCompositionPreview.SetElementChildVisual(Me, visual)
    End Sub

    Public Async Function StartCaptureAsync() As Task
        ' The GraphicsCapturePicker follows the same pattern the
        ' file pickers do.
        Dim picker As New GraphicsCapturePicker
        Dim item As GraphicsCaptureItem = Await picker.PickSingleItemAsync()

        ' The item may be null if the user dismissed the
        ' control without making a selection or hit Cancel.
        If item IsNot Nothing Then
            StartCaptureInternal(item)
        End If
    End Function

    Private Sub StartCaptureInternal(item As GraphicsCaptureItem)
        ' Stop the previous capture if we had one.
        StopCapture()

        CaptureItem = item
        _lastSize = CaptureItem.Size

        FramePool = Direct3D11CaptureFramePool.Create(
            _canvasDevice, ' D3D device
            DirectXPixelFormat.B8G8R8A8UIntNormalized, ' Pixel format
            2, '  Number of frames
            CaptureItem.Size) ' Size of the buffers

        _session = FramePool.CreateCaptureSession(CaptureItem)
        _session.StartCapture()
    End Sub

    Private Sub FramePool_FrameArrived(sender As Direct3D11CaptureFramePool, args As Object) Handles FramePool.FrameArrived
        ' The FrameArrived event is raised for every frame on the thread
        ' that created the Direct3D11CaptureFramePool. This means we
        ' don't have to do a null-check here, as we know we're the only
        ' one dequeueing frames in our application.  

        ' NOTE Disposing the frame retires it And returns  
        ' the buffer to the pool.

        Using frame = FramePool.TryGetNextFrame()
            ProcessFrame(frame)
        End Using
    End Sub

    Private Sub CaptureItem_Closed(sender As GraphicsCaptureItem, args As Object) Handles CaptureItem.Closed
        StopCapture()
    End Sub

    Public Sub StopCapture()
        _session?.Dispose()
        FramePool?.Dispose()
        CaptureItem = Nothing
        _session = Nothing
        FramePool = Nothing
    End Sub

    Private Sub ProcessFrame(frame As Direct3D11CaptureFrame)
        ' Resize and device-lost leverage the same function on the
        ' Direct3D11CaptureFramePool. Refactoring it this way avoids
        ' throwing in the catch block below (device creation could always
        ' fail) along with ensuring that resize completes successfully And
        ' isn't vulnerable to device-lost.

        Dim needsReset As Boolean = False
        Dim recreateDevice As Boolean = False

        If (frame.ContentSize.Width <> _lastSize.Width) OrElse
            (frame.ContentSize.Height <> _lastSize.Height) Then
            needsReset = True
            _lastSize = frame.ContentSize
        End If

        Try
            ' Take the D3D11 surface and draw it into a  
            ' Composition surface.

            ' Convert our D3D11 surface into a Win2D object.
            Dim bitmap = CanvasBitmap.CreateFromDirect3D11Surface(
                _canvasDevice,
                frame.Surface)

            ' Helper that handles the drawing for us.
            FillSurfaceWithBitmap(bitmap)
            ' This is the device-lost convention for Win2D.
        Catch e As Exception When _canvasDevice.IsDeviceLost(e.HResult)
            ' We lost our graphics device. Recreate it and reset
            ' our Direct3D11CaptureFramePool.  
            needsReset = True
            recreateDevice = True
        End Try

        If needsReset Then
            ResetFramePool(frame.ContentSize, recreateDevice)
        End If
    End Sub

    Private Sub FillSurfaceWithBitmap(canvasBitmap As CanvasBitmap)
        CanvasComposition.Resize(_surface, canvasBitmap.Size)

        Using session = CanvasComposition.CreateDrawingSession(_surface)
            session.Clear(Colors.Transparent)
            session.DrawImage(canvasBitmap)
        End Using
    End Sub

    Private Sub ResetFramePool(size As SizeInt32, recreateDevice As Boolean)
        Do
            Try
                If recreateDevice Then
                    _canvasDevice = New CanvasDevice()
                End If
                FramePool.Recreate(_canvasDevice, DirectXPixelFormat.B8G8R8A8UIntNormalized, 2, size)
                ' This is the device-lost convention for Win2D.
            Catch e As Exception When _canvasDevice.IsDeviceLost(e.HResult)
                _canvasDevice = Nothing
                recreateDevice = True
            End Try
        Loop While _canvasDevice Is Nothing
    End Sub

    Private Async Sub Button_ClickAsync(sender As Object, e As RoutedEventArgs) Handles CaptureButton.Click
        Await StartCaptureAsync()
    End Sub

End Class

Grabar un vídeo

Si desea grabar un vídeo de la aplicación, puede seguir el tutorial que se presenta en el artículo Captura de pantalla en vídeo. O bien, puedes usar el espacio de nombresWindows.Media.AppRecording. Esto forma parte del SDK de extensión de escritorio, por lo que solo funciona en escritorios de Windows y requiere que agregue una referencia a él desde el proyecto. Consulte Programación con SDK de extensiónpara obtener más información.

Consulte también