Freigeben über


Bildschirmaufnahme

Ab Windows 10, Version 1803, stellt der Windows.Graphics.Capture Namespace APIs zum Abrufen von Frames aus einem Anzeige- oder Anwendungsfenster bereit, um Videostreams oder Momentaufnahmen zu erstellen, um zusammenarbeitende und interaktive Erfahrungen zu erstellen.

Mit der Bildschirmaufnahme rufen Entwickler die sichere Systembenutzeroberfläche für Endbenutzer auf, um das aufzuzeichnende Anzeige- oder Anwendungsfenster auszuwählen. Ein gelber Benachrichtigungsrahmen wird vom System um das aktiv erfasste Element gezeichnet. Bei mehreren gleichzeitigen Aufnahmesitzungen wird um jedes erfasste Element ein gelber Rahmen gezeichnet.

Hinweis

Die Bildschirmaufnahme-APIs werden nur auf Windows-Geräten und immersiven Windows Mixed Reality-Headsets unterstützt.

In diesem Artikel wird das Erfassen eines einzelnen Bilds des Anzeige- oder Anwendungsfensters beschrieben. Informationen zum Codieren von Frames, die vom Bildschirm in eine Videodatei aufgenommen werden, finden Sie unter Bildschirmaufnahme zu Video

Hinzufügen der Bildschirmaufnahmefunktion

Die APIs im Windows.Graphics.Capture Namespace erfordern eine allgemeine Funktion, die im Manifest Ihrer Anwendung deklariert werden muss:

  1. Öffnen Sie Package.appxmanifest im Projektmappen-Explorer.
  2. Wählen Sie die Registerkarte Fähigkeiten aus.
  3. Überprüfen Sie Grafikaufnahme.

Grafikerfassung

System-Benutzeroberfläche starten, um die Bildschirmaufnahme zu beginnen.

Bevor Sie die System-UI starten, können Sie überprüfen, ob Ihre Anwendung derzeit Bildschirmaufnahmen erstellen kann. Es gibt mehrere Gründe, warum Ihre Anwendung möglicherweise keine Bildschirmaufnahme durchführen kann, zum Beispiel wenn das Gerät die Hardwareanforderungen nicht erfüllt oder wenn die zu erfassende Anwendung die Bildschirmaufnahme blockiert. Verwenden Sie die Methode IsSupported in der Klasse GraphicsCaptureSession, um zu ermitteln, ob die Bildschirmaufnahme in UWP unterstützt wird:

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

Nachdem Sie überprüft haben, dass die Bildschirmaufnahme unterstützt wird, verwenden Sie die Klasse GraphicsCapturePicker, um den System-Auswahldialog aufzurufen. Der Endbenutzer verwendet diese Benutzeroberfläche, um das Anzeige- oder Anwendungsfenster auszuwählen, in dem Bildschirmaufnahmen aufgenommen werden sollen. Das Auswahlfenster gibt ein GraphicsCaptureItem- zurück, das zur Erstellung einer GraphicsCaptureSession-verwendet wird.

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

Da es sich um UI-Code handelt, muss er im UI-Thread aufgerufen werden. Wenn Sie sie über den CodeBehind für eine Seite Ihrer Anwendung (z. B. MainPage.xaml.cs) aufrufen, erfolgt dies automatisch, aber wenn nicht, können Sie erzwingen, dass sie im UI-Thread mit dem folgenden Code ausgeführt wird:

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

Einen Aufnahme-Framepool und eine Aufnahmesitzung erstellen

Mit dem GraphicsCaptureItem-erstellen Sie ein Direct3D11CaptureFramePool- mit Ihrem D3D-Gerät, unterstütztes Pixelformat (DXGI_FORMAT_B8G8R8A8_UNORM), die Anzahl der gewünschten Frames (die eine beliebige ganze Zahl sein können) und die Framegröße. Die ContentSize Eigenschaft der GraphicsCaptureItem Klasse kann als Größe Ihres Frames verwendet werden.

Hinweis

Auf Systemen mit aktivierter Windows HD-Farbe ist das Inhaltspixelformat möglicherweise nicht unbedingt DXGI_FORMAT_B8G8R8A8_UNORM. Um Pixel-Überclipping zu vermeiden (d. h. der aufgenommene Inhalt sieht ausgewaschen aus), wenn HDR-Inhalte erfasst werden, erwägen Sie die Verwendung von DXGI_FORMAT_R16G16B16A16_FLOAT für jede Komponente in der Aufnahmepipeline, einschließlich des Direct3D11CaptureFramePool, dem Ziel wie CanvasBitmap. Je nach Bedarf sind möglicherweise zusätzliche Verarbeitungen erforderlich, z. B. das Speichern im HDR-Inhaltsformat oder die HDR-zu-SDR-Tonzuordnung. Dieser Artikel wird sich auf die Aufzeichnung von SDR-Inhalten fokussieren. Weitere Informationen finden Sie unter Verwenden von DirectX mit High-Dynamic-Range-Displays und erweiterten Farben.

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

Rufen Sie als Nächstes eine Instanz der Klasse GraphicsCaptureSession für Ihren Direct3D11CaptureFramePool ab, indem Sie das GraphicsCaptureItem an die Methode CreateCaptureSession übergeben.

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

Sobald der Benutzer explizit zugestimmt hat, ein Anwendungsfenster oder eine Anzeige in der Systembenutzeroberfläche zu erfassen, kann das GraphicsCaptureItem- mehreren CaptureSession- Objekten zugeordnet werden. Auf diese Weise kann Ihre Anwendung dasselbe Element für verschiedene Erfahrungen erfassen.

Um mehrere Elemente gleichzeitig zu erfassen, muss Ihre Anwendung eine Aufnahmesitzung für jedes element erstellen, das erfasst werden muss, was das Aufrufen der Auswahl-UI für jedes Element erfordert, das erfasst werden soll.

Abrufen von Aufnahmeframes

Rufen Sie mit dem erstellten Framepool und der Aufnahmesitzung die StartCapture-Methode für Ihre GraphicsCaptureSession-Instanz auf, um das System zu benachrichtigen, dass die Aufnahmebilder an Ihre App gesendet werden sollen.

_session.StartCapture();
_session.StartCapture()

Um diese Aufnahmeframes abzurufen, die als Direct3D11CaptureFrame-Objekte vorliegen, können Sie das Direct3D11CaptureFramePool.FrameArrived-Ereignis verwenden:

_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

Es wird empfohlen, die Verwendung des UI-Threads zu vermeiden, wenn möglich für FrameArrived, da dieses Ereignis jedes Mal ausgelöst wird, wenn ein neuer Frame verfügbar ist, was häufig ist. Wenn Sie sich entscheiden, FrameArrived- im UI-Thread zu hören, achten Sie darauf, wie viel Arbeit Sie bei jedem Auslösen des Ereignisses ausführen.

Alternativ können Sie Frames mit der Direct3D11CaptureFramePool.TryGetNextFrame Methode manuell abrufen, bis Sie alle benötigten Frames erhalten.

Das Direct3D11CaptureFrame--Objekt enthält die Eigenschaften ContentSize, Surfaceund SystemRelativeTime-. Die SystemRelativeTime- ist die QPC-Zeit (QueryPerformanceCounter), die zur Synchronisierung anderer Medienelemente verwendet werden kann.

Erfassungsrahmen für Prozesse

Jeder Frame aus dem Direct3D11CaptureFramePool wird ausgecheckt, wenn TryGetNextFrame-aufgerufen und entsprechend der Lebensdauer des Direct3D11CaptureFrame--Objekts wieder eingecheckt wird. Für systemeigene Anwendungen reicht es aus, das Direct3D11CaptureFrame-Objekt freizugeben, um den Frame wieder in den Framepool zurückzugeben. Für verwaltete Anwendungen wird empfohlen, die Methode Direct3D11CaptureFrame.Dispose (Close in C++) zu verwenden. Direct3D11CaptureFrame implementiert die IClosable Schnittstelle, die als IDisposable für C#-Aufrufer projiziert wird.

Anwendungen sollten keine Verweise auf Direct3D11CaptureFrame- -Objekte speichern, noch sollten sie Verweise auf die zugrunde liegende Direct3D-Oberfläche speichern, nachdem der Frame wieder eingecheckt wurde.

Beim Verarbeiten eines Frames wird empfohlen, dass Anwendungen die Sperre ID3D11Multithread auf demselben Gerät verwenden, das mit dem Direct3D11CaptureFramePool-Objekt verknüpft ist.

Die zugrunde liegende Direct3D-Oberfläche hat immer die Größe, die beim Erstellen (oder erneuten Erstellen) des Direct3D11CaptureFramePoolangegeben wurde. Wenn der Inhalt größer als der Rahmen ist, wird der Inhalt auf die Größe des Rahmens beschnitten. Wenn der Inhalt kleiner als der Frame ist, enthält der Rest des Frames nicht definierte Daten. Es wird empfohlen, dass Anwendungen ein Unterrechteck mithilfe der ContentSize-Eigenschaft für dieses Direct3D11CaptureFrame- kopieren, um zu vermeiden, dass nicht definierte Inhalte angezeigt werden.

Screenshot erstellen

In unserem Beispiel konvertieren wir jedes Direct3D11CaptureFrame in eine CanvasBitmap, die Teil der „Win2D-APIs”ist.

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

Sobald wir die CanvasBitmap-haben, können wir sie als Bilddatei speichern. Im folgenden Beispiel speichern wir sie als PNG-Datei im Ordner „Gespeicherte Bilder“ des Benutzers im „ Ordner“.

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

Reagieren auf Größenänderung von Elementen oder Geräteverlust

Während des Erfassungsprozesses möchten Anwendungen möglicherweise Aspekte ihres Direct3D11CaptureFramePool ändern. Dazu gehört auch die Bereitstellung eines neuen Direct3D-Geräts, das Ändern der Größe der Framepuffer oder sogar das Ändern der Anzahl der Puffer innerhalb des Pools. In jedem dieser Szenarien ist die Methode "Neu erstellen" für das Direct3D11CaptureFramePool-Objekt das empfohlene Tool.

Wenn Neu erstellen aufgerufen wird, werden alle vorhandenen Frames verworfen. Dadurch wird verhindert, dass Frames weitergegeben werden, deren zugrunde liegende Direct3D-Oberflächen zu einem Gerät gehören, auf das die Anwendung möglicherweise keinen Zugriff mehr hat. Aus diesem Grund kann es sinnvoll sein, alle ausstehenden Frames zu verarbeiten, bevor Sie Neu erstellenaufrufen.

Alles zusammensetzen

Der folgende Codeausschnitt ist ein End-to-End-Beispiel für die Implementierung der Bildschirmaufnahme in einer UWP-Anwendung. In diesem Beispiel haben wir zwei Schaltflächen im Front-End: eine ruft Button_ClickAsyncauf, und die andere ruft ScreenshotButton_ClickAsyncauf.

Hinweis

Dieser Codeausschnitt verwendet Win2D-, eine Bibliothek für das Rendern von 2D-Grafiken. In der Dokumentation finden Sie Informationen dazu, wie Sie es für Ihr Projekt einrichten.

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

Aufzeichnen eines Videos

Wenn Sie ein Video Ihrer Anwendung aufzeichnen möchten, können Sie der Anleitung im Artikel Bildschirmaufnahme zu Videofolgen. Oder, Sie können auch Windows.Media.AppRecording-Namespaceverwenden. Dies ist Teil des Desktoperweiterungs-SDK, sodass es nur auf Windows-Desktops funktioniert und erfordert, dass Sie einen Verweis darauf aus Ihrem Projekt hinzufügen. Weitere Informationen finden Sie unter Programmierung mit Erweiterungs-SDKs.

Siehe auch