Bildschirmaufnahme

Beginnend mit Windows 10, Version 1803, enthält der Windows.Graphics.Capture-Namespace APIs zum Abrufen von Frames aus einer Anzeige oder einem Anwendungsfenster, um Videostreams oder gemeinsame und interaktive Erfahrungen zu erstellen.

Bei der Bildschirmaufnahme rufen Entwickler eine sichere Systembenutzeroberfläche auf, damit Endbenutzer das zu erfassende Anzeige- oder Anwendungsfenster auswählen können. Außerdem wird vom System ein gelber Benachrichtigungsrahmen um das aktiv erfasste Element gezeichnet. Bei mehreren gleichzeitigen Aufnahmesitzungen wird ein gelber Rahmen um jedes zu erfassende Element 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 zurCodierung von Frames, die vom Bildschirm in eine Videodatei erfasst werden, finden Sie unter Bildschirmaufnahme in 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 Funktionen aus.
  3. Aktivieren Sie die Option Grafikerfassung.

Graphics Capture

Starten der Systembenutzeroberfläche zum Starten der Bildschirmaufnahme

Vor dem Starten der Systembenutzeroberfläche können Sie überprüfen, ob Ihre Anwendung derzeit Bildschirmaufnahmen erstellen kann. Es kann mehrere Gründe dafür geben, weshalb Ihre Anwendung die Bildschirmaufnahmefunktion möglicherweise nicht verwenden kann, z. B. wenn das Gerät die Hardwareanforderungen nicht erfüllt, oder wenn die Anwendung für die Erfassung die Bildschirmaufnahmefunktion blockiert. Verwenden Sie die IsSupported-Methode in der GraphicsCaptureSession-Klasse, um zu bestimmen, ob die UWP-Bildschirmaufnahme 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 sich vergewissert haben, dass die Bildschirmaufnahmefunktion unterstützt wird, verwenden Sie die GraphicsCapturePicker-Klasse, um die Systemauswahlbenutzeroberfläche aufzurufen. Der Endbenutzer verwendet diese Benutzeroberfläche, um das Anzeige- oder Anwendungsfenster auszuwählen, für das Bildschirmaufnahmen erstellt werden sollen. Die Auswahl gibt ein GraphicsCaptureItem zurück, das zum Erstellen 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 Benutzeroberflächencode handelt, muss er im Benutzeroberflächenthread aufgerufen werden. Wenn Sie ihn aus dem CodeBehind für eine Seite Ihrer Anwendung (z. B. MainPage.xaml.cs) aufrufen, erfolgt dies automatisch. Andernfalls können Sie die Ausführung im Benutzeroberflächenthread mit dem folgenden Code erzwingen:

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

Erstellen eines Aufnahmeframepools und einer Aufnahmesitzung

Mithilfe der GraphicsCaptureItem-Klasse erstellen Sie eine Direct3D11CaptureFramePool-Klasse mit Ihrem D3D-Gerät, dem unterstützten Pixelformat (DXGI_FORMAT_B8G8R8A8_UNORM), der Anzahl der gewünschten Frames (in Form einer beliebigen ganzen Zahl) und der Framegröße. Die ContentSize-Eigenschaft der GraphicsCaptureItem-Klasse kann als Größe des Frames verwendet werden:

Hinweis

Auf Systemen mit aktivierter Windows HD-Farbe ist das Inhaltspixelformat möglicherweise nicht unbedingt DXGI_FORMAT_B8G8R8A8_UNORM. Um Pixelüberschneidungen beim Aufnehmen von HDR-Inhalten zu vermeiden (in solchen Fällen wirkt der erfasste Inhalt verwaschen), sollten Sie DXGI_FORMAT_R16G16B16A16_FLOAT für jede Komponente in der Aufnahmepipeline verwenden, einschließlich der Direct3D11CaptureFramePool-Klasse und Zielen wie CanvasBitmap. Je nach Bedarf können zusätzliche Verarbeitungsschritte erforderlich sein, z. B. das Speichern im HDR-Inhaltsformat oder eine HDR-zu-SDR-Tonzuordnung. Dieser Artikel konzentriert sich auf die Erfassung von SDR-Inhalten. Weitere Informationen finden Sie unter Verwenden von DirectX mit HDR-Monitoren und dem Farbmodus „Erweiterte Farbe“.

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 GraphicsCaptureSession-Klasse für die Direct3D11CaptureFramePool-Klasse ab, indem Sie das GraphicsCaptureItem an die CreateCaptureSession-Methode übergeben:

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

Nachdem der Benutzer in der Systembenutzeroberfläche explizit seine Zustimmung zum Erfassen eines Anzeige oder Anwendungsfenster erteilt hat, 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 aufzunehmen, muss Ihre Anwendung eine Aufnahmesitzung für jedes zu erfassende Element erstellen. Dies erfordert das Aufrufen der Auswahlbenutzeroberfläche für jedes Element, das erfasst werden soll.

Abrufen von Aufnahmeframes

Rufen Sie nach dem Erstellen des Framepools und der Aufnahmesitzung die StartCapture-Methode in Ihrer GraphicsCaptureSession-Instanz auf, um das System zu benachrichtigen, mit dem Senden von Aufnahmeframes an Ihre App zu beginnen:

_session.StartCapture();
_session.StartCapture()

Zum Abrufen dieser Aufnahmeframes, bei denen es sich um Direct3D11CaptureFrame-Objekte handelt, 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 Benutzeroberflächenthreads für das FrameArrived-Ereignis nach Möglichkeit zu vermeiden, da es jedes Mal ausgelöst wird, wenn ein neuer Frame verfügbar ist, was häufig vorkommen dürfte. Wenn Sie sich dafür entscheiden, im Benutzeroberflächethread nach dem FrameArrived-Ereignis zu lauschen, achten Sie darauf, wie viel Arbeit dies bei jedem Auslösen des Ereignisses erfordert.

Alternativ können Sie Frames mithilfe der Direct3D11CaptureFramePool.TryGetNextFrame-Methode manuell mithilfe von Pull übertragen, bis Sie alle benötigten Frames erhalten haben.

Das Direct3D11CaptureFrame-Objekt enthält die Eigenschaften ContentSize, Surface und SystemRelativeTime. SystemRelativeTime ist die QPC-Zeit (QueryPerformanceCounter), die zum Synchronisieren anderer Medienelemente verwendet werden kann.

Prozesserfassungsframes

Jeder Frame aus der Direct3D11CaptureFramePool-Klasse wird beim Aufrufen von TryGetNextFrame ausgecheckt und entsprechend der Lebensdauer des Direct3D11CaptureFrame-Objekts wieder eingecheckt. Bei nativen Anwendungen reicht die Freigabe des Direct3D11CaptureFrame-Objekts aus, um den Frame wieder im Framepool einzuchecken. Für verwaltete Anwendungen wird empfohlen, die Direct3D11CaptureFrame.Dispose-Methode (Close in C++) zu verwenden. Direct3D11CaptureFrame implementiert die IClosable-Schnittstelle, die für C#-Aufrufer als IDisposable projiziert wird.

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

Bei der Verarbeitung eines Frames wird empfohlen, dass Anwendungen die ID3D11Multithread-Sperre auf demselben Gerät verwenden, das dem Direct3D11CaptureFramePool-Objekt zugeordnet ist.

Die zugrunde liegende Direct3D-Oberfläche entspricht immer der Größe, die beim Erstellen (oder erneuten Erstellen) der Direct3D11CaptureFramePool-Klasse angegeben wurde. Wenn der Inhalt größer als der Frame ist, wird er auf die Größe des Frames zugeschnitten. Ist der Inhalt kleiner als der Frame, enthält sein Rest undefinierte Daten. Es wird empfohlen, dass Anwendungen mithilfe der ContentSize-Eigenschaft ein Sub-Rect für diese Direct3D11CaptureFrame-Klasse kopieren, um das Anzeigen nicht definierter Inhalte zu vermeiden.

Erstellen eines Screenshots

In unserem Beispiel konvertieren wir jeden Direct3D11CaptureFrame in ein CanvasBitmap, das Teil der Win2D-APIs ist.

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

Sobald das CanvasBitmap verfügbar ist, kann es als Bilddatei gespeichert werden. Im folgenden Beispiel speichern wir es als PNG-Datei im Gespeicherte Bilder-Ordner des Benutzers.

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

React zum Erfassen der Größenänderung von Elementen oder verlorener Geräten

Während des Aufnahmeprozesses möchten Anwendungen möglicherweise Aspekte ihrer Direct3D11CaptureFramePool-Klasse ändern. Dies umfasst die Bereitstellung eines neuen Direct3D-Geräts, das Ändern der Größe der Framepuffer oder sogar der Anzahl von Puffern innerhalb des Pools. Für jedes dieser Szenarien wird die Recreate-Methode für das Direct3D11CaptureFramePool-Objekt empfohlen.

Wenn die Recreate-Methode aufgerufen wird, werden alle vorhandenen Frames verworfen. Dadurch soll die Weitergabe von Frames verhindert 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 ratsam sein, alle ausstehenden Frames zu verarbeiten, bevor Sie die Recreate-Methode aufrufen.

Alles zusammenfügen

Der folgende Codeausschnitt ist ein End-to-End-Beispiel für die Implementierung der Bildschirmaufnahme in einer UWP-Anwendung. In diesem Beispiel sind zwei Schaltflächen im Front-End verfügbar: eine ruft Button_ClickAsync, die andere ScreenshotButton_ClickAsync auf.

Hinweis

Dieses Codeschnipsel verwendet Win2D, eine Bibliothek zum Rendern von 2D-Grafiken. Informationen zum Einrichten dieser Bibliothek für Ihr Projekt finden Sie in der zugehörigen Dokumentation.

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 die exemplarische Vorgehensweise befolgen, die im Artikel Screenshot in Video beschrieben wird. Alternativ können Sie den Windows.Media.AppRecording-Namespace verwenden. Er ist Teil des Desktoperweiterungs-SDKs und funktioniert daher nur auf Windows-Desktops. Darüber hinaus müssen Sie aus Ihrem Projekt darauf verweisen. Weitere Informationen finden Sie unter Programmieren mit Erweiterungs-SDKs.

Siehe auch