Delen via


Schermopname

Vanaf Windows 10 versie 1803 biedt de naamruimte Windows.Graphics.Capture API's om frames te verkrijgen uit een weergave- of toepassingsvenster, om videostreams of momentopnamen te maken om gezamenlijke en interactieve ervaringen te bouwen.

Met schermopname roepen ontwikkelaars beveiligde systeemgebruikersinterface aan voor eindgebruikers om het scherm of toepassingsvenster te kiezen dat moet worden vastgelegd en wordt een gele meldingsrand getekend door het systeem rond het actief vastgelegde item. In het geval van meerdere gelijktijdige opnamesessies wordt een gele rand rond elk item getekend dat wordt vastgelegd.

Opmerking

De schermafbeeldings-API's worden alleen ondersteund op Windows-apparaten en Windows Mixed Reality immersive headsets.

In dit artikel wordt beschreven hoe u één afbeelding van het weergave- of toepassingsvenster vastlegt. Zie Schermopname naar video voor informatie over het coderen van frames die zijn vastgelegd van het scherm naar een videobestand

De mogelijkheid voor schermopname toevoegen

Voor de API's in de naamruimte Windows.Graphics.Capture moet een algemene mogelijkheid worden gedeclareerd in het manifest van uw toepassing:

  1. Open Package.appxmanifest in de Solution Explorer.
  2. Selecteer het tabblad Capaciteiten.
  3. Controleer Graphics Capture.

Grafische Capture

Start de systeeminterface om schermopname te starten

Voordat u de systeemgebruikersinterface start, kunt u controleren of uw toepassing momenteel schermopnamen kan maken. Er zijn verschillende redenen waarom uw toepassing mogelijk geen schermopname kan gebruiken, waaronder als het apparaat niet voldoet aan de hardwarevereisten of als de toepassing die is bedoeld voor het vastleggen van schermafbeeldingen blokkeert. Gebruik de methode IsSupported in de klasse GraphicsCaptureSession om te bepalen of UWP-schermopname wordt ondersteund:

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

Zodra u hebt gecontroleerd of schermopname wordt ondersteund, gebruikt u de klasse GraphicsCapturePicker om de gebruikersinterface van de systeemkiezer aan te roepen. De eindgebruiker gebruikt deze gebruikersinterface om het weergave- of toepassingsvenster te selecteren waarvan u schermopnamen wilt maken. De selectietool retourneert een GraphicsCaptureItem- dat wordt gebruikt om een GraphicsCaptureSession-te genereren:

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

Omdat dit UI-code is, moet deze worden aangeroepen in de UI-thread. Als u dit aanroept vanuit het code-behind-bestand voor een pagina van uw toepassing (zoals MainPage.xaml.cs), wordt dit automatisch voor u gedaan, maar als dat niet zo is, kunt u afdwingen dat het wordt uitgevoerd op de UI-thread met de volgende code:

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

Een captureframepool maken en sessie vastleggen

Met behulp van GraphicsCaptureItem maakt u een Direct3D11CaptureFramePool met uw D3D-apparaat, ondersteunde pixelindeling (DXGI_FORMAT_B8G8R8A8_UNORM), het aantal gewenste frames (dat elk geheel getal kan zijn) en de framegrootte. De eigenschap ContentSize van de klasse GraphicsCaptureItem kan worden gebruikt als de grootte van uw frame:

Opmerking

Op systemen waarvoor Windows HD-kleur is ingeschakeld, is het pixelformaat van de inhoud wellicht niet DXGI_FORMAT_B8G8R8A8_UNORM. Als u pixeloverschrijving wilt vermijden (bijvoorbeeld wanneer de vastgelegde inhoud eruit gewassen wordt) bij het vastleggen van HDR-inhoud, kunt u overwegen om DXGI_FORMAT_R16G16B16A16_FLOAT te gebruiken voor elk onderdeel in de opnametijdlijn, inclusief de Direct3D11CaptureFramePoolen de doelbestemming zoals CanvasBitmap. Afhankelijk van de behoefte is extra verwerking, zoals opslaan in HDR-inhoudsindeling of HDR-naar-SDR-toontoewijzing, mogelijk vereist. In dit artikel wordt aandacht besteed aan het vastleggen van SDR-inhoud. Zie voor meer informatie DirectX gebruiken met hoog dynamisch bereik beeldschermen en geavanceerde kleur.

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

Haal vervolgens een instantie op van de GraphicsCaptureSession-klasse voor uw Direct3D11CaptureFramePool door de GraphicsCaptureItem door te geven aan de CreateCaptureSession methode.

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

Zodra de gebruiker expliciet toestemming heeft gegeven voor het vastleggen van een toepassingsvenster of weergave in de systeeminterface, kan de GraphicsCaptureItem worden gekoppeld aan meerdere CaptureSession-objecten . Op deze manier kan uw toepassing ervoor kiezen om hetzelfde item vast te leggen voor verschillende ervaringen.

Als u meerdere items tegelijk wilt vastleggen, moet uw toepassing een opnamesessie maken voor elk item dat moet worden vastgelegd. Hiervoor moet u de gebruikersinterface van de kiezer aanroepen voor elk item dat moet worden vastgelegd.

Opnamebeelden verkrijgen

Wanneer uw framepool en opnamesessie zijn gemaakt, roept u de Methode StartCapture aan op uw GraphicsCaptureSession-exemplaar om het systeem op de hoogte te stellen van het verzenden van opnameframes naar uw app:

_session.StartCapture();
_session.StartCapture()

Om deze captureframes te verkrijgen, die Direct3D11CaptureFrame-objecten zijn, kunt u gebruikmaken van de Direct3D11CaptureFramePool.FrameArrived-gebeurtenis.

_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

Het wordt aanbevolen om het gebruik van de UI-thread indien mogelijk te vermijden voor FrameArrived, omdat deze gebeurtenis wordt gegenereerd telkens wanneer een nieuw frame beschikbaar is, wat regelmatig zal zijn. Als u ervoor kiest om te luisteren naar FrameArrived- op de UI-thread, moet u rekening houden met hoeveel werk u doet wanneer de gebeurtenis wordt geactiveerd.

U kunt ook handmatig frames ophalen met de Methode Direct3D11CaptureFramePool.TryGetNextFrame totdat u alle frames krijgt die u nodig hebt.

Het Object Direct3D11CaptureFrame bevat de eigenschappen ContentSize, Surface en SystemRelativeTime. De SystemRelativeTime is QPC-tijd (QueryPerformanceCounter) die kan worden gebruikt om andere media-elementen te synchroniseren.

Frames voor procesopname

Elk frame van de Direct3D11CaptureFramePool wordt uitgecheckt bij het aanroepen van TryGetNextFrameen ingecheckt op basis van de levensduur van het Direct3D11CaptureFrame object. Bij systeemeigen toepassingen is het vrijgeven van het Direct3D11CaptureFrame object voldoende om het frame weer terug te geven aan de framebuffer. Voor beheerde toepassingen wordt aanbevolen om de methode Direct3D11CaptureFrame.Dispose (Sluiten in C++) te gebruiken. Direct3D11CaptureFrame implementeert de interface IClosable, die wordt geprojecteerd als IDisposable voor C#-gebruikers.

Toepassingen mogen geen verwijzingen opslaan naar Direct3D11CaptureFrame-objecten en mogen ook geen verwijzingen opslaan naar het onderliggende Direct3D-oppervlak nadat het frame weer is ingecheckt.

Tijdens het verwerken van een frame wordt aanbevolen dat toepassingen de ID3D11Multithread-vergrendeling gebruiken op hetzelfde apparaat dat is gekoppeld aan het Direct3D11CaptureFramePool-object.

Het onderliggende Direct3D-oppervlak is altijd de grootte die is opgegeven bij het maken (of opnieuw maken) van de Direct3D11CaptureFramePool. Als de inhoud groter is dan het frame, wordt de inhoud geknipt tot de grootte van het frame. Als de inhoud kleiner is dan het frame, bevat de rest van het frame niet-gedefinieerde gegevens. Het wordt aanbevolen dat toepassingen een sub-rect kopiëren met behulp van de eigenschap ContentSize voor die Direct3D11CaptureFrame om te voorkomen dat niet-gedefinieerde inhoud wordt weergegeven.

Een schermopname maken

In ons voorbeeld converteren we elk Direct3D11CaptureFrame naar een CanvasBitmap, die deel uitmaakt van de Win2D-API's.

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

Zodra we de CanvasBitmap hebben, kunnen we deze opslaan als een afbeeldingsbestand. In het volgende voorbeeld slaan we het op als EEN PNG-bestand in de map Opgeslagen afbeeldingen van de gebruiker.

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

Reageren op het aanpassen van de grootte van een item of apparaatverlies

Tijdens het vastleggen kunnen toepassingen aspecten van hun Direct3D11CaptureFramePool wijzigen. Dit omvat het leveren van een nieuw Direct3D-apparaat, het wijzigen van de grootte van de framebuffers of zelfs het wijzigen van het aantal buffers in de pool. In elk van deze scenario's is de OpnieuwMaken-methode van het Direct3D11CaptureFramePool-object het aanbevolen hulpprogramma.

Wanneer Recreate wordt aangeroepen, worden alle bestaande frames genegeerd. Dit is om te voorkomen dat frames worden uitgedeeld waarvan de onderliggende Direct3D-oppervlakken deel uitmaken van een apparaat waartoe de toepassing mogelijk geen toegang meer heeft. Daarom kan het een goed idee zijn om alle in behandeling zijnde frames te verwerken voordat u hercreërenaanroept.

Alles samenvoegen

Het volgende codefragment is een end-to-end-voorbeeld van het implementeren van schermopnamen in een UWP-toepassing. In dit voorbeeld hebben we twee knoppen in de front-end: één roept Button_ClickAsyncaan, en de andere roept ScreenshotButton_ClickAsyncaan.

Opmerking

Dit fragment maakt gebruik van Win2D, een bibliotheek voor 2D-grafische rendering. Raadpleeg de bijbehorende documentatie voor informatie over het instellen ervan voor uw project.

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

Een video opnemen

Als u een video van uw toepassing wilt opnemen, kunt u het overzicht volgen dat wordt weergegeven in het artikel Schermopname voor video. U kunt ook de naamruimte Windows.Media.AppRecording gebruiken. Dit maakt deel uit van de Desktop-extensie-SDK, dus het werkt alleen op Windows-bureaubladen en vereist dat u er een verwijzing naar toevoegt vanuit uw project. Zie Programmeren met extensie-SDK's voor meer informatie.

Zie ook