Bildschirmaufnahme

Die Windows. Graphics.Capture Namespace stellt APIs zum Abrufen von Frames aus einem Anzeige- oder Anwendungsfenster bereit, um Videostreams oder Momentaufnahmen zu erstellen, um zusammenarbeits- und interaktive Erfahrungen zu erstellen.

Mit der Bildschirmaufnahme rufen Entwickler die sichere Systembenutzeroberfläche für Endbenutzer auf, um das anzuzeigende Anzeige- oder Anwendungsfenster zu wählen, und 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.

Note

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

In diesem Artikel wird das Erfassen eines einzelnen Bilds des Anzeige- oder Anwendungsfensters beschrieben.

Prüfen, ob Bildschirmaufnahmen unterstützt werden

Bevor Sie versuchen, den Bildschirm aufzunehmen, prüfen Sie, ob das aktuelle Gerät Bildschirmaufnahmen unterstützt. Verwenden Sie die IsSupported-Methode für GraphicsCaptureSession , um zu ermitteln, ob die Bildschirmaufnahme verfügbar ist:

// Check if screen capture is supported
if (!GraphicsCaptureSession.IsSupported())
{
    // Hide capture UI
    CaptureControlsPanel.Visibility = Visibility.Collapsed;
    PreviewPlaceholderText.Text = "Screen capture isn't supported on this device.";
    UpdateStatus("Screen capture isn't supported on this device.");
    return;
}

Es gibt mehrere Gründe, warum die Bildschirmaufnahme möglicherweise nicht unterstützt wird, z. B. wenn das Gerät die Hardwareanforderungen nicht erfüllt.

Starten der System-UI zum Starten der Bildschirmaufnahme

Verwenden Sie die GraphicsCapturePicker-Klasse, um die Systemauswahl-Benutzeroberfläche aufzurufen. Der Endbenutzer verwendet diese Benutzeroberfläche, um das Anzeige- oder Anwendungsfenster auszuwählen, das erfasst werden soll. Der Auswahl-Dialog gibt ein GraphicsCaptureItem zurück, mit dem eine Aufnahmesitzung erstellt wird.

In einer WinUI 3-App müssen Sie den Picker mit dem Fensterhandle initialisieren, bevor Sie PickSingleItemAsync aufrufen:

// Launch picker and start capture
var picker = new GraphicsCapturePicker();
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
GraphicsCaptureItem item = await picker.PickSingleItemAsync();
if (item != null)
{
    StartCaptureInternal(item);
}

Aufnahme-Framepool und Aufnahmesitzung erstellen

Erstellen Sie mithilfe des GraphicsCaptureItem-Objekts ein Direct3D11CaptureFramePool mit Ihrem D3D-Gerät, einem unterstützten Pixelformat (DXGI_FORMAT_B8G8R8A8_UNORM), der Anzahl der gewünschten Frames (die eine beliebige ganze Zahl sein kann) und die Framegröße. Die Size-Eigenschaft der GraphicsCaptureItem-Klasse kann als Größe des Frames verwendet werden:

// Create frame pool and capture session
_framePool = Direct3D11CaptureFramePool.Create(
    _canvasDevice,
    CaptureDirectXPixelFormat.B8G8R8A8UIntNormalized,
    BufferCount,
    item.Size);
_session = _framePool.CreateCaptureSession(item);

Note

Auf Systemen mit aktiviertem Windows HD Color muss das Pixelformat des Inhalts nicht unbedingt DXGI_FORMAT_B8G8R8A8_UNORM sein. Um pixelüberclipping zu vermeiden (d. h. der aufgenommene Inhalt sieht ausgewaschen) beim Aufnehmen von HDR-Inhalten, sollten Sie in Betracht ziehen, DXGI_FORMAT_R16G16B16A16_FLOAT für jede Komponente in der Aufnahmepipeline zu verwenden, einschließlich des Direct3D11CaptureFramePool, des Zielziels 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 konzentriert sich auf die SDR-Inhaltserfassung. Weitere Informationen finden Sie unter Verwenden von DirectX mit HDR-Anzeigen und erweiterter Farbdarstellung.

Sobald der Benutzer explizit seine Zustimmung zum Erfassen eines Anwendungsfensters oder einer Anzeige in der System-UI erteilt hat, kann das GraphicsCaptureItem-Objekt mehreren Aufnahmesitzungen zugeordnet werden. Auf diese Weise kann Ihre Anwendung dasselbe Element für verschiedene Nutzungserlebnisse erfassen.

Abrufen von Aufnahmeframes

Nachdem Ihr Framepool und Ihre Erfassungssitzung erstellt wurden, rufen Sie für Ihre GraphicsCaptureSession-Instanz StartCapture auf, um das System anzuweisen, mit dem Senden von Erfassungsframes an Ihre App zu beginnen.

Verwenden Sie zum Abrufen dieser Aufnahmeframes, die Direct3D11CaptureFrame-Objekte sind, das Direct3D11CaptureFramePool.FrameArrived-Ereignis :

// Handle frame arrival
_framePool.FrameArrived += OnFrameArrived;

Es wird empfohlen, im Handler von FrameArrived keine aufwendigen Arbeiten auf dem UI-Thread auszuführen, da dieses Ereignis jedes Mal ausgelöst wird, wenn ein neuer Frame verfügbar ist. Wenn Sie sich dafür entscheiden, im UI-Thread auf FrameArrived zu lauschen, achten Sie darauf, wie viel Arbeit Sie jedes Mal ausführen, wenn das Ereignis ausgelöst wird.

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, Surface und SystemRelativeTime. SystemRelativeTime ist QPC (QueryPerformanceCounter)-Zeit, die zum Synchronisieren anderer Medienelemente verwendet werden kann.

Erfasste Prozessbilder

Jeder Frame aus dem Direct3D11CaptureFramePool wird beim Aufrufen von TryGetNextFrame ausgecheckt und entsprechend der Lebensdauer des Direct3D11CaptureFrame-Objekts wieder eingecheckt. Für verwaltete Anwendungen wird empfohlen, die Direct3D11CaptureFrame.Dispose-Methode zu verwenden. Direct3D11CaptureFrame implementiert IDisposable, sodass das Entfernen des Frames den Puffer an den Pool zurückgibt.

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

In diesem Beispiel wird jeder Frame in eine CanvasBitmap konvertiert, die Teil der Win2D-Bibliothek ist:

// Convert frame to Win2D bitmap and display
CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromDirect3D11Surface(
    _canvasDevice,
    frame.Surface);

Die zugrunde liegende Direct3D-Oberfläche hat immer die beim Erstellen (oder erneuten Erstellen) des Direct3D11CaptureFramePool angegebene Größe. Wenn der Inhalt größer als der Rahmen ist, wird er auf die Größe des Rahmens zugeschnitten. 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.

Speichern eines Screenshots

Sobald Sie über eine CanvasBitmap verfügen, können Sie sie als Bilddatei speichern. Im folgenden Beispiel wird der aktuelle Frame als PNG-Datei mithilfe einer FileSavePicker gespeichert. In einer WinUI 3-App müssen Sie den Picker mit dem Fensterhandle initialisieren:

// Save screenshot
var savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
savePicker.SuggestedFileName = "screen-capture";
savePicker.FileTypeChoices.Add("PNG image", new List<string> { ".png" });
WinRT.Interop.InitializeWithWindow.Initialize(savePicker, _hwnd);
StorageFile? file = await savePicker.PickSaveFileAsync();
if (file is not null)
{
    using var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite);
    await frameToSave.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
}

Auf Größenänderung eines Elements oder Geräteverlust reagieren

Während des Aufnahmevorgangs 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 "Recreate" -Methode für das Direct3D11CaptureFramePool-Objekt der empfohlene Ansatz.

Wenn "Neu erstellen" aufgerufen wird, werden alle vorhandenen Frames verworfen. Dadurch wird verhindert, dass Frames ausgegeben werden, deren zugrunde liegende Direct3D-Oberflächen zu einem Gerät gehören, auf das die Anwendung möglicherweise nicht mehr zugreifen kann. Aus diesem Grund kann es sinnvoll sein, alle ausstehenden Frames vor dem Aufrufen von "Neu erstellen" zu verarbeiten.

Überlegungen zu WinUI 3

Beachten Sie beim Migrieren von Bildschirmaufnahmecode von UWP zu WinUI 3 (Windows App SDK) die folgenden Unterschiede:

  • Initialisierung des Fensterhandles — Picker wie GraphicsCapturePicker und FileSavePicker müssen mithilfe von InitializeWithWindow mit dem Fensterhandle initialisiert werden. Weitere Informationen finden Sie unter Abrufen eines Fensterhandles (HWND).
  • Kompositions-APIs im UI-Thread – In WinUI 3 müssen CanvasComposition-Oberflächenvorgänge (z. B. Zeichnen auf eine CompositionDrawingSurface) mithilfe von DispatcherQueue.TryEnqueue an den UI-Thread verteilt werden. Frameerfassung und Bitmaperstellung kann im Hintergrundthread des Framepools erfolgen, aber das Aktualisieren der Visuellen Komposition muss im UI-Thread erfolgen.
  • Namespace changes – Verwenden Sie Microsoft.UI.Composition, Microsoft.UI.Xaml.Hosting und Microsoft.UI.Dispatching anstelle ihrer Windows.UI-Entsprechungen. Der namespace Windows.Graphics.Capture bleibt unverändert.

Siehe auch