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:
- Öffnen Sie Package.appxmanifest im Projektmappen-Explorer.
- Wählen Sie die Registerkarte Funktionen aus.
- Aktivieren Sie die Option Grafikerfassung.
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.