Compartilhar via


Captura de tela

A partir do Windows 10, versão 1803, o namespace Windows.Graphics.Capture fornece APIs para adquirir quadros de uma janela de exibição ou aplicativo, para criar fluxos de vídeo ou instantâneos para criar experiências colaborativas e interativas.

Com a captura de tela, os desenvolvedores invocam a interface do usuário segura do sistema para que os usuários finais escolham a janela de exibição ou aplicativo a ser capturada, e uma borda de notificação amarela é desenhada pelo sistema ao redor do item capturado ativamente. No caso de várias sessões de captura simultâneas, uma borda amarela é desenhada ao redor de cada item que está sendo capturado.

Observação

As APIs de captura de tela só são suportadas em dispositivos Windows e fones de ouvido imersivos Realidade Misturada do Windows.

Este artigo descreve a captura de uma única imagem da janela de exibição ou aplicativo. Para obter informações sobre como codificar quadros capturados da tela para um arquivo de vídeo, consulte Captura de tela para vídeo

Adicionar o recurso de captura de tela

As APIs encontradas no namespace Windows.Graphics.Capture exigem um recurso geral a ser declarado no manifesto do aplicativo:

  1. Abra Package.appxmanifest no Gerenciador de Soluções.
  2. Selecione a guia Funcionalidades.
  3. Marque Captura de gráficos.

Captura de Gráficos

Inicie a interface do usuário do sistema para iniciar a captura de tela

Antes de iniciar a interface do usuário do sistema, você pode verificar se seu aplicativo é atualmente capaz de fazer capturas de tela. Há vários motivos pelos quais seu aplicativo pode não ser capaz de usar a captura de tela, inclusive se o dispositivo não atender aos requisitos de hardware ou se o aplicativo direcionado para captura bloqueia a captura de tela. Use o método IsSupported na classe GraphicsCaptureSession para determinar se a captura de tela UWP é suportada:

// 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

Depois de verificar se a captura de tela é suportada, use a classe GraphicsCapturePicker para invocar a interface do usuário do seletor do sistema. O usuário final usa essa interface do usuário para selecionar a janela de exibição ou aplicativo da qual deseja fazer capturas de tela. O seletor retornará um GraphicsCaptureItem que será usado para criar um 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

Como esse é o código da interface do usuário, ele precisa ser chamado no thread da interface do usuário. Se você estiver chamando-o a partir do code-behind de uma página do seu aplicativo (como MainPage.xaml.cs), isso será feito para você automaticamente, mas se não, você poderá forçá-lo a ser executado no thread da interface do usuário com o seguinte código:

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())

Criar um pool de quadros de captura e uma sessão de captura

Usando o GraphicsCaptureItem, você criará um Direct3D11CaptureFramePool com seu dispositivo D3D, formato de pixel (DXGI_FORMAT_B8G8R8A8_UNORM) suportado, número de quadros desejados (que pode ser qualquer inteiro) e tamanho do quadro. A propriedade ContentSize da classe GraphicsCaptureItem pode ser usada como o tamanho do quadro:

Observação

Em sistemas com cores HD do Windows habilitadas, o formato de pixel de conteúdo pode não ser necessariamente DXGI_FORMAT_B8G8R8A8_UNORM. Para evitar a sobrecarga de pixels (ou seja, o conteúdo capturado parece desbotado) ao capturar conteúdo HDR, considere o uso de DXGI_FORMAT_R16G16B16A16_FLOAT para cada componente no pipeline de captura, incluindo o Direct3D11CaptureFramePool, o destino de destino, como CanvasBitmap. Dependendo da necessidade, pode ser necessário processamento adicional, como salvar no formato de conteúdo HDR ou mapeamento de tom HDR para SDR. Este artigo se concentrará na captura de conteúdo SDR. Para obter mais informações, consulte Usando DirectX com monitores de alto alcance dinâmico e cores avançadas.

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

Em seguida, obtenha uma instância da classe GraphicsCaptureSession para seu Direct3D11CaptureFramePool passando o GraphicsCaptureItem para o método CreateCaptureSession:

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

Depois que o usuário tiver dado consentimento explícito para capturar uma janela do aplicativo ou exibição na interface do usuário do sistema, o GraphicsCaptureItem poderá ser associado a vários objetos CaptureSession. Dessa forma, seu aplicativo pode optar por capturar o mesmo item para várias experiências.

Para capturar vários itens ao mesmo tempo, seu aplicativo deve criar uma sessão de captura para cada item a ser capturado, o que requer invocar a interface do usuário do seletor para cada item a ser capturado.

Adquirir quadros de captura

Com o pool de quadros e a sessão de captura criados, chame o método StartCapture em sua instância GraphicsCaptureSession para notificar o sistema para começar a enviar quadros de captura para seu aplicativo:

_session.StartCapture();
_session.StartCapture()

Para adquirir esses quadros de captura, que são objetos Direct3D11CaptureFrame você pode usar o 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

É recomendável evitar o uso do thread da interface do usuário, se possível, para FrameArrived, pois esse evento será gerado sempre que um novo quadro estiver disponível, o que será frequente. Se você optar por ouvir FrameArrived no thread da interface do usuário, esteja atento à quantidade de trabalho que você está fazendo sempre que o evento é acionado.

Como alternativa, você pode puxar quadros manualmente com o método Direct3D11CaptureFramePool.TryGetNextFrame até obter todos os quadros necessários.

O objeto Direct3D11CaptureFrame contém as propriedades ContentSize, Surface e SystemRelativeTime. O SystemRelativeTime é o tempo QPC (QueryPerformanceCounter) que pode ser usado para sincronizar outros elementos de mídia.

Processar quadros de captura

Cada quadro do Direct3D11CaptureFramePool é submetido a check-out ao chamar TryGetNextFrame e verificado novamente de acordo com o tempo de vida do objeto Direct3D11CaptureFrame. Para aplicativos nativos, liberar o objeto Direct3D11CaptureFrame é suficiente para verificar o quadro de volta no pool de quadros. Para aplicativos gerenciados, é recomendável usar o método Direct3D11CaptureFrame.Dispose (Close em C++). Direct3D11CaptureFrame implementa a interface IClosable que é projetada como IDisposable para chamadores C#.

Os aplicativos não devem salvar referências a objetos Direct3D11CaptureFrame nem devem salvar referências à superfície subjacente do Direct3D após o check-in do quadro ter sido feito novamente.

Durante o processamento de um quadro, é recomendável que os aplicativos façam o bloqueio ID3D11Multithread no mesmo dispositivo associado ao objeto Direct3D11CaptureFramePool.

A superfície subjacente do Direct3D sempre terá o tamanho especificado ao criar (ou recriar) o Direct3D11CaptureFramePool. Se o conteúdo for maior que o quadro, o conteúdo será cortado para o tamanho do quadro. Se o conteúdo for menor que o quadro, o restante do quadro conterá dados indefinidos. É recomendável que os aplicativos copiem um sub-rect usando a propriedade ContentSize para esse Direct3D11CaptureFrame para evitar a exibição de conteúdo indefinido.

Fazer uma captura de tela

Em nosso exemplo, convertemos cada Direct3D11CaptureFrame em um CanvasBitmap, que faz parte das APIs Win2D.

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

Depois de termos o CanvasBitmap, podemos salvá-lo como um arquivo de imagem. No exemplo a seguir, o salvamos como um arquivo PNG na pasta Imagens salvas do usuário.

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);
}

Reaja para capturar o redimensionamento do item ou a perda do dispositivo

Durante o processo de captura, os aplicativos podem desejar alterar aspectos de suas Direct3D11CaptureFramePool. Isso inclui fornecer um novo dispositivo Direct3D, alterar o tamanho dos buffers de quadros ou até mesmo alterar o número de buffers dentro do pool. Em cada um desses cenários, o método Recreate no objeto Direct3D11CaptureFramePool é a ferramenta recomendada.

Quando Recriate é chamado, todos os quadros existentes são descartados. Isso é para evitar a distribuição de quadros cujas superfícies Direct3D subjacentes pertencem a um dispositivo ao qual o aplicativo pode não ter mais acesso. Por esse motivo, pode ser aconselhável processar todos os quadros pendentes antes de chamar Recriate.

Como reunir tudo

O trecho de código a seguir é um exemplo completo de como implementar a captura de tela em um aplicativo UWP. Neste exemplo, temos dois botões no front-end: um chama Button_ClickAsync e o outro chama ScreenshotButton_ClickAsync.

Observação

Este trecho usa Win2D, uma biblioteca para renderização de gráficos 2D. Consulte a documentação deles para informações sobre como configurá-lo para seu projeto.

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

Gravar um vídeo

Se você quiser gravar um vídeo do seu aplicativo, você pode seguir o passo a passo apresentado no artigo Captura de tela para vídeo. Ou, você pode usar o namespace Windows.Media.AppRecording. Isso faz parte do SDK de extensão da área de trabalho, portanto, só funciona em áreas de trabalho do Windows e requer que você adicione uma referência a ele do seu projeto. Consulte Programação com SDKs de extensão para mais informações.

Confira também