Partager via


Capture d'écran

À compter de Windows 10 version 1803, l’espace de noms Windows.Graphics.Capture fournit des API pour l’acquisition d’images à partir d’une fenêtre d’affichage ou d’application, afin de créer des flux vidéo ou des captures instantanées pour créer des expériences collaboratives et interactives.

Avec la capture d’écran, les développeurs appellent l’interface utilisateur système sécurisée pour que les utilisateurs finaux sélectionnent la fenêtre d’affichage ou d’application à capturer, et une bordure de notification jaune est dessinée par le système autour de l’élément activement capturé. Dans le cas de plusieurs sessions de capture simultanée, une bordure jaune est dessinée autour de chaque élément capturé.

Remarque

Les API de capture d’écran ne sont prises en charge que sur les appareils Windows et les casques immersifs Windows Mixed Reality.

Cet article décrit la capture d’une seule image de la fenêtre d’affichage ou d’application. Pour plus d’informations sur l’encodage des images capturées à partir de l’écran dans un fichier vidéo, consultez Capture d’écran sur vidéo

Ajouter la fonctionnalité de capture d’écran

Les API trouvées dans l’espace de noms Windows.Graphics.Capture nécessitent une fonctionnalité générale pour être déclarées dans le manifeste de votre application :

  1. Ouvrez Package.appxmanifest dans l’Explorateur de solutions.
  2. Sélectionnez l’onglet Fonctionnalités.
  3. Cochez Capture graphique.

Capture graphique

Lancer l’interface utilisateur système pour démarrer la capture d’écran

Avant de lancer l’interface utilisateur système, vous pouvez vérifier si votre application est actuellement en mesure de prendre des captures d’écran. Il existe plusieurs raisons pour lesquelles votre application peut ne pas être en mesure d’utiliser la capture d’écran, notamment si l’appareil ne répond pas aux exigences matérielles ou si l’application ciblée pour la capture bloque la capture d’écran. Utilisez la méthode IsSupported dans la classe GraphicsCaptureSession pour déterminer si la capture d’écran UWP est prise en charge :

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

Une fois que vous avez vérifié que la capture d’écran est prise en charge, utilisez la classe GraphicsCapturePicker pour appeler l’interface utilisateur du sélecteur de système. L’utilisateur final utilise cette interface utilisateur pour sélectionner la fenêtre d’affichage ou d’application dont il souhaite effectuer des captures d’écran. Le sélecteur retourne un GraphicsCaptureItem qui est utilisé pour créer une 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

Étant donné qu’il s’agit du code d’interface utilisateur, il doit être appelé sur le thread de l’interface utilisateur. Si vous l’appelez à partir du code-behind pour une page de votre application (comme MainPage.xaml.cs), cette opération est effectuée automatiquement pour vous, mais si ce n’est pas le cas, vous pouvez la forcer à s’exécuter sur le thread de l’interface utilisateur avec le code suivant :

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

Créer un pool d’images de capture et une session de capture

À l’aide de GraphicsCaptureItem, vous allez créer un Direct3D11CaptureFramePool avec votre appareil D3D, le format de pixel pris en charge (DXGI_FORMAT_B8G8R8A8_UNORM), le nombre d’images souhaitées (qui peut être n’importe quel entier) et la taille de l’image. La propriété ContentSize de la classe GraphicsCaptureItem peut être utilisée comme la taille de votre image :

Remarque

Sur les systèmes avec la couleur Windows HD activée, le format de pixel de contenu peut ne pas nécessairement être DXGI_FORMAT_B8G8R8A8_UNORM. Pour éviter le surdécoupage des pixels (c’est-à-dire que le contenu capturé semble délavé) lors de la capture de contenu HDR, envisagez d’utiliser DXGI_FORMAT_R16G16B16A16_FLOAT pour chaque composant du pipeline de capture, y compris Direct3D11CaptureFramePool, la destination cible telle que CanvasBitmap. Selon le besoin, un traitement supplémentaire, tel que l’enregistrement au format de contenu HDR ou le mappage de tonalité HDR à SDR, peut être nécessaire. Cet article se concentre sur la capture de contenu SDR. Pour plus d’informations, veuillez consulter Utilisation de DirectX avec des affichages à plage dynamique élevée et des couleurs avancées.

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

Ensuite, obtenez un instance de la classe GraphicsCaptureSession pour votre Direct3D11CaptureFramePool en passant GraphicsCaptureItem à la méthode CreateCaptureSession :

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

Une fois que l’utilisateur a explicitement donné son consentement pour capturer une fenêtre d’application ou un affichage dans l’interface utilisateur système, GraphicsCaptureItem peut être associé à plusieurs objets CaptureSession. De cette façon, votre application peut choisir de capturer le même élément pour différentes expériences.

Pour capturer plusieurs éléments en même temps, votre application doit créer une session de capture pour chaque élément à capturer, ce qui nécessite d’appeler l’interface utilisateur du sélecteur pour chaque élément à capturer.

Acquérir des images de capture

Une fois votre pool d’images et votre session de capture créés, appelez la méthode StartCapture sur votre instance GraphicsCaptureSession pour avertir le système de commencer à envoyer des images de capture à votre application :

_session.StartCapture();
_session.StartCapture()

Pour acquérir ces images de capture, qui sont des objets Direct3D11CaptureFrame, vous pouvez utiliser l’événement 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

Il est recommandé d’éviter si possible d’utiliser le thread de l’interface utilisateur pour FrameArrived, car cet événement est déclenché chaque fois qu’une nouvelle image est disponible, ce qui sera fréquent. Si vous choisissez d’écouter FrameArrived sur le thread de l’interface utilisateur, gardez à l’esprit tout le travail que vous effectuez chaque fois que l’événement se déclenche.

Sinon, vous pouvez extraire manuellement des images avec la méthode Direct3D11CaptureFramePool.TryGetNextFrame jusqu’à ce que vous obteniez toutes les images dont vous avez besoin.

L’objet Direct3D11CaptureFrame contient les propriétés ContentSize, Surface et SystemRelativeTime. SystemRelativeTime correspond au temps QPC (QueryPerformanceCounter) qui peut être utilisé pour synchroniser d’autres éléments multimédias.

Traiter les images de capture

Chaque image de Direct3D11CaptureFramePool est extraite lors de l’appel de TryGetNextFrame, puis ré-archivée en fonction de la durée de vie de l’objet Direct3D11CaptureFrame. Pour les applications natives, la libération de l’objet Direct3D11CaptureFrame suffit pour ré-archiver l’image dans le pool d’images. Pour les applications managées, il est recommandé d’utiliser la méthode Direct3D11CaptureFrame.Dispose (Close dans C++). Direct3D11CaptureFrame implémente l’interface IClosable, qui est projetée comme IDisposable pour les appelants C#.

Les applications ne doivent pas enregistrer les références aux objets Direct3D11CaptureFrame, ni enregistrer les références à la surface Direct3D sous-jacente une fois l’image ré-archivée.

Lors du traitement d’une image, il est recommandé que les applications prennent le verrou ID3D11Multithread sur le même appareil que celui associé à l’objet Direct3D11CaptureFramePool.

La surface Direct3D sous-jacente sera toujours de la taille spécifiée lors de la création (ou de la recréation) de Direct3D11CaptureFramePool. Si le contenu est plus grand que le cadre, le contenu est détouré à la taille du cadre. Si le contenu est plus petit que le cadre, le reste de l’image contient des données non définies. Il est recommandé que les applications copient un sous-rectangle à l’aide de la propriété ContentSize pour Direct3D11CaptureFrame afin d’éviter l’affichage du contenu non défini.

Effectuer une capture d’écran

Dans notre exemple, nous convertissons chaque Direct3D11CaptureFrame en canvasBitmap, qui fait partie des API Win2D.

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

Une fois CanvasBitmap obtenu, nous pouvons l’enregistrer en tant que fichier image. Dans l’exemple suivant, nous l’enregistrons en tant que fichier PNG dans le dossier Images enregistrées de l’utilisateur.

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

Réagir pour capturer le redimensionnement d’élément ou l’appareil perdu

Pendant le processus de capture, les applications peuvent souhaiter modifier certains aspects de leur Direct3D11CaptureFramePool. Cela inclut fournir un nouvel appareil Direct3D, modifier la taille des mémoires tampons de trame ou modifier le nombre de mémoires tampons au sein du pool. Dans chacun de ces scénarios, la méthode Recreate sur l’objet Direct3D11CaptureFramePool est l’outil recommandé.

Lorsque Recreate est appelé, tous les images existantes sont ignorées. Cela permet d’empêcher la remise d’images dont les surfaces Direct3D sous-jacentes appartiennent à un appareil auquel l’application n’a peut-être plus accès. Pour cette raison, il peut être judicieux de traiter toutes les images en attente avant d’appeler Recreate.

Pour résumer

L’extrait de code suivant est un exemple de bout en bout illustrant comment implémenter la capture d’écran dans une application UWP. Dans cet exemple, nous avons deux boutons dans le front-end : l’un appelle Button_ClickAsync et l’autre appelle ScreenshotButton_ClickAsync.

Remarque

Cet extrait de code utilise Win2D, une bibliothèque pour le rendu graphique 2D. Consultez leur documentation pour plus d’informations sur la façon de la configurer pour votre projet.

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

Enregistrer une vidéo

Si vous souhaitez enregistrer une vidéo de votre application, vous pouvez suivre la procédure pas à pas présentée dans l’article Capture d’écran sur vidéo. Vous pouvez également utiliser l’espace de noms Windows.Media.AppRecording. Cela fait partie du Kit de développement logiciel (SDK) de l’extension Desktop. Il fonctionne uniquement sur les bureaux Windows et nécessite que vous y ajoutiez une référence à partir de votre projet. Pour plus d’informations, consultez Programmation avec des Kit de développement logiciel (SDK) d’extension.

Voir aussi