Acquisizione schermo

Iniziando da Windows 10, version 1803, lo spazio dei nomi Windows.Graphics.Capture fornisce API per l'acquisizione di fotogrammi da una schermata o finestra dell'applicazione, per creare flussi video o snapshot per esperienze di collaborazione e interattive.

Con l'acquisizione dello schermo, gli sviluppatori richiamano l'interfaccia utente di sistema sicura per consentire agli utenti finali di selezionare la finestra di visualizzazione o dell'applicazione da acquisire e un bordo di notifica giallo viene disegnato dal sistema intorno all'elemento acquisito attivamente. Nel caso di più sessioni di acquisizione simultanee, viene disegnato un bordo giallo intorno a ogni elemento acquisito.

Nota

Le API di acquisizione dello schermo sono supportate solo nei dispositivi Windows e nei visori VR immersivi Windows Mixed Reality.

Questo articolo descrive l'acquisizione di una singola immagine della finestra di visualizzazione o applicazione. Per informazioni sulla codifica dei fotogrammi acquisiti dallo schermo a un file video, vedere Acquisizione dello schermo nel video

Aggiungere la funzionalità di acquisizione dello schermo

Le API disponibili nello spazio dei nomi Windows.Graphics.Capture richiedono una funzionalità generale da dichiarare nel manifesto dell'applicazione:

  1. Aprire Package.appxmanifest in Solution Explorer.
  2. Fare clic sulla scheda Funzionalità.
  3. Controllare l'acquisizione grafica.

Acquisizione grafica

Avviare l'interfaccia utente di sistema per avviare l'acquisizione della schermata

Prima di avviare l'interfaccia utente di sistema, è possibile verificare se l'applicazione è attualmente in grado di acquisire schermate. Esistono diversi motivi per cui l'applicazione potrebbe non essere in grado di usare l'acquisizione dello schermo, tra cui se il dispositivo non soddisfa i requisiti hardware o se l'applicazione è destinata all'acquisizione dello schermo blocchi di acquisizione. Usare il metodo IsSupported nella classe GraphicsCaptureSession per determinare se l'acquisizione di schermata UWP è supportata:

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

Dopo aver verificato che l'acquisizione dello schermo è supportata, usare la classe GraphicsCapturePicker per richiamare l'interfaccia utente del selettore di sistema. L'utente finale usa questa interfaccia utente per selezionare la finestra di visualizzazione o applicazione di cui acquisire le schermate. La selezione restituirà un elemento GraphicsCaptureItem che verrà usato per creare un oggetto 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

Poiché si tratta di codice dell'interfaccia utente, deve essere chiamato nel thread dell'interfaccia utente. Se viene chiamato dal code-behind per una pagina dell'applicazione (ad esempio MainPage.xaml.cs) questa operazione viene eseguita automaticamente, ma in caso contrario, è possibile forzarla per l'esecuzione nel thread dell'interfaccia utente con il codice seguente:

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

Creare un pool di frame di acquisizione e una sessione di acquisizione

Usando GraphicsCaptureItem, si creerà un oggetto Direct3D11CaptureFramePool con il dispositivo D3D, il formato pixel supportato (DXGI_FORMAT_B8G8R8A8_UNORM), il numero di fotogrammi desiderati (che può essere qualsiasi numero intero) e le dimensioni del fotogramma. La proprietà ContentSize della classe GraphicsCaptureItem può essere utilizzata come dimensione del frame:

Nota

Nei sistemi con il colore Windows HD abilitato, il formato pixel del contenuto potrebbe non essere necessariamente DXGI_FORMAT_B8G8R8A8_UNORM. Per evitare l'overclip dei pixel (ad esempio, il contenuto acquisito sembra lavato) durante l'acquisizione di contenuto HDR, è consigliabile usare DXGI_FORMAT_R16G16B16A16_FLOAT per ogni componente nella pipeline di acquisizione, incluso Direct3D11CaptureFramePool, la destinazione target, ad esempio CanvasBitmap. Dipende dalla necessità, potrebbe essere necessaria un'elaborazione aggiuntiva, ad esempio il salvataggio in formato di contenuto HDR o il mapping dei toni HDR-to-SDR. Questo articolo è incentrato sull'acquisizione di contenuto SDR. Per ulteriori informazioni, vedere Utilizzo di DirectX con schermi ad alta gamma dinamica e Advanced Color.

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

Ottenere quindi un'istanza della classe GraphicsCaptureSession per Direct3D11CaptureFramePool passando GraphicsCaptureItem al metodo CreateCaptureSession:

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

Dopo che l'utente ha concesso esplicitamente il consenso per acquisire una finestra dell'applicazione o visualizzare nell'interfaccia utente di sistema, GraphicsCaptureItem può essere associato a più oggetti CaptureSession. In questo modo l'applicazione può scegliere di acquisire lo stesso elemento per diverse esperienze.

Per acquisire più elementi contemporaneamente, l'applicazione deve creare una sessione di acquisizione per ogni elemento da acquisire, che richiede di richiamare l'interfaccia utente di selezione per ogni elemento che deve essere acquisito.

Acquisire fotogrammi di acquisizione

Dopo aver creato il pool di frame e la sessione di acquisizione, chiamare il metodo StartCapture nell'istanza GraphicsCaptureSession per inviare al sistema una notifica per avviare l'invio di fotogrammi di acquisizione all'app:

_session.StartCapture();
_session.StartCapture()

Per acquisire questi fotogrammi di acquisizione, ovvero oggetti Direct3D11CaptureFrame, è possibile usare l'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

È consigliabile evitare di usare il thread dell'interfaccia utente, se possibile per FrameArrived, perché questo evento verrà generato ogni volta che è disponibile un nuovo frame, che sarà frequente. Se si sceglie di ascoltare FrameArrived nel thread dell'interfaccia utente, tenere presente quanto lavoro si sta facendo ogni volta che viene generato l'evento.

In alternativa, è possibile eseguire manualmente il pull dei fotogrammi con il metodo Direct3D11CaptureFramePool.TryGetNextFrame fino a ottenere tutti i fotogrammi necessari.

L'oggetto Direct3D11CaptureFrame contiene le proprietà ContentSize, Surface e SystemRelativeTime. SystemRelativeTime è l'ora QPC (QueryPerformanceCounter) che può essere usata per sincronizzare altri elementi multimediali.

Elaborare fotogrammi di acquisizione

Ogni fotogramma di Direct3D11CaptureFramePool viene estratto quando si chiama TryGetNextFrame e viene eseguito il checkback in base alla durata dell'oggetto Direct3D11CaptureFrame. Per le applicazioni native, il rilascio dell'oggetto Direct3D11CaptureFrame è sufficiente per controllare il frame di nuovo nel pool di frame. Per le applicazioni gestite, è consigliabile usare il metodo Direct3D11CaptureFrame.Dispose (Close in C++). Direct3D11CaptureFrame implementa l'interfaccia IClosable, proiettata come IDisposable per i chiamanti C#.

Le applicazioni non devono salvare i riferimenti agli oggetti Direct3D11CaptureFrame, né devono salvare riferimenti alla superficie Direct3D sottostante dopo il checkback del fotogramma.

Durante l'elaborazione di un frame, è consigliabile che le applicazioni prendano il blocco ID3D11Multithread sullo stesso dispositivo associato all'oggetto Direct3D11CaptureFramePool.

La superficie Direct3D sottostante sarà sempre la dimensione specificata durante la creazione (o la ricreazione) di Direct3D11CaptureFramePool. Se il contenuto è maggiore del frame, il contenuto viene ritagliato in base alle dimensioni del frame. Se il contenuto è inferiore al frame, il resto del frame contiene dati non definiti. È consigliabile copiare le applicazioni da una sottosezione usando la proprietà ContentSize per Direct3D11CaptureFrame per evitare di visualizzare contenuto non definito.

Acquisire uno screenshot

In questo esempio, ogni Direct3D11CaptureFrame viene convertito in un CanvasBitmap, che è parte di Win2D APIs.

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

Dopo aver creato CanvasBitmap, è possibile salvarlo come file immagine. Nell'esempio seguente viene salvato come file PNG nella cartella Immagini salvate dell'utente.

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

Reagire per acquisire il ridimensionamento o la perdita del dispositivo

Durante il processo di acquisizione, le applicazioni possono voler modificare gli aspetti di Direct3D11CaptureFramePool. Ciò include la fornitura di un nuovo dispositivo Direct3D, la modifica delle dimensioni dei buffer dei frame o anche la modifica del numero di buffer all'interno del pool. In ognuno di questi scenari, il metodo Recreate nell'oggetto Direct3D11CaptureFramePool è lo strumento consigliato.

Quando viene chiamato Ricreate, tutti i fotogrammi esistenti vengono eliminati. Ciò impedisce di distribuire fotogrammi le cui superfici Direct3D sottostanti appartengono a un dispositivo a cui l'applicazione potrebbe non avere più accesso. Per questo motivo, può essere opportuno elaborare tutti i fotogrammi in sospeso prima di chiamare Ricreate.

Combinazione delle funzionalità

Il frammento di codice seguente è un esempio end-to-end di come implementare l'acquisizione dello schermo in un'applicazione UWP. In questo esempio sono disponibili due pulsanti nel front-end: uno chiama Button_ClickAsync e l'altro chiama ScreenshotButton_ClickAsync.

Nota

Questo frammento di codice usa Win2D, una libreria per il rendering della grafica 2D. Vedere la relativa documentazione per informazioni su come configurarla per il progetto.

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

Registrare un video

Se si vuole registrare un video dell'applicazione, è possibile seguire la procedura dettagliata presentata nell'articolo Acquisizione dello schermo nel video. In alternativa, è possibile usare lo spazio dei nomi Windows.Media.AppRecording. Questo fa parte dell'SDK dell'estensione desktop, quindi funziona solo nei desktop di Windows e richiede l'aggiunta di un riferimento a esso dal progetto. Per altre informazioni, vedere Programmazione con estensione SDK.

Vedi anche