Udostępnij przez


Przechwytywanie ekranu

Począwszy od systemu Windows 10 w wersji 1803, przestrzeń nazw Windows.Graphics.Capture udostępnia interfejsy API do uzyskiwania ramek z ekranu lub okna aplikacji, umożliwiając tworzenie strumieni wideo lub migawek do budowania środowisk współpracy i interaktywnych doświadczeń.

Podczas przechwytywania ekranu deweloperzy wywołują bezpieczny interfejs użytkownika systemu, aby użytkownicy końcowi mogli wybrać okno wyświetlania lub aplikacji do przechwycenia, a żółte obramowanie powiadomień jest rysowane przez system wokół aktywnie przechwyconego elementu. W przypadku wielu równoczesnych sesji przechwytywania, wokół każdego przechwytywanego elementu rysowane jest żółte obramowanie.

Uwaga / Notatka

Interfejsy API do przechwytywania ekranu są obsługiwane wyłącznie na urządzeniach z systemem Windows oraz immersyjnych zestawach nagłownych Windows Mixed Reality.

W tym artykule opisano przechwytywanie pojedynczego obrazu okna wyświetlania lub aplikacji. Aby uzyskać informacje na temat kodowania ramek przechwyconych z ekranu do pliku wideo, zobacz Przechwytywanie ekranu do wideo

Dodawanie możliwości przechwytywania ekranu

Interfejsy API znajdujące się w przestrzeni nazw Windows.Graphics.Capture wymagają zadeklarowania ogólnej możliwości w manifeście aplikacji:

  1. Otwórz Package.appxmanifest w Eksploratorze rozwiązań .
  2. Wybierz kartę Możliwości .
  3. Sprawdź funkcji przechwytywania grafiki.

przechwytywanie grafiki

Uruchom interfejs użytkownika systemu, aby rozpocząć przechwytywanie ekranu

Przed uruchomieniem interfejsu użytkownika systemu możesz sprawdzić, czy aplikacja jest obecnie w stanie przechwycić ekrany. Istnieje kilka powodów, dla których aplikacja może nie być w stanie używać przechwytywania ekranu, w tym jeśli urządzenie nie spełnia wymagań sprzętowych lub jeżeli aplikacja, którą próbuje się przechwycić, blokuje przechwytywanie ekranu. Użyj metody IsSupported w klasie GraphicsCaptureSession, aby określić, czy przechwytywanie ekranu platformy UWP jest obsługiwane:

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

Po sprawdzeniu, czy przechwytywanie ekranu jest obsługiwane, użyj klasy GraphicsCapturePicker, aby wywołać interfejs użytkownika selektora systemu. Użytkownik końcowy używa tego interfejsu użytkownika do wybierania okna wyświetlania lub aplikacji do przechwytywania ekranu. Selektor zwróci GraphicsCaptureItem, który zostanie użyty do utworzenia GraphicsCaptureSession:

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

Ponieważ jest to kod interfejsu użytkownika, należy go wywołać w wątku interfejsu użytkownika. Jeśli wywołujesz go z kodu-behind strony twojej aplikacji (na przykład MainPage.xaml.cs), jest to wykonywane automatycznie, ale jeśli nie, możesz wymusić jego uruchomienie na wątku interfejsu użytkownika przy użyciu następującego kodu:

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

Utworzyć pulę ramek przechwytywania i sesję przechwytywania

Korzystając z GraphicsCaptureItem, utworzysz Direct3D11CaptureFramePool z urządzeniem D3D, obsługiwanym formatem pikseli (DXGI_FORMAT_B8G8R8A8_UNORM), liczbą żądanych ramek (które mogą być dowolną liczbą całkowitą) i rozmiarem ramki. Właściwość ContentSize klasy GraphicsCaptureItem może być używana jako rozmiar twojej ramki.

Uwaga / Notatka

W systemach z włączonym kolorem Windows HD, format pikseli zawartości może nie koniecznie być DXGI_FORMAT_B8G8R8A8_UNORM. Aby uniknąć przeklupowania pikseli (tj. przechwycona zawartość wygląda wyprana z kolorów) podczas przechwytywania zawartości HDR, rozważ użycie formatu DXGI_FORMAT_R16G16B16A16_FLOAT dla każdego składnika w potoku przechwytywania, w tym dla Direct3D11CaptureFramePooloraz dla miejsca docelowego, takiego jak CanvasBitmap. Zależy od potrzeb, może być wymagane dodatkowe przetwarzanie, takie jak zapisywanie w formacie zawartości HDR lub mapowanie tonu HDR-to-SDR. Ten artykuł koncentruje się na przechwytywaniu zawartości SDR. Aby uzyskać więcej informacji, zobacz Korzystanie z DirectX z wyświetlaczami o wysokim zakresie dynamicznym i zaawansowanymi kolorami.

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

Następnie uzyskaj instancję klasy GraphicsCaptureSession dla swojego Direct3D11CaptureFramePool, przekazując GraphicsCaptureItem do metody CreateCaptureSession:

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

Gdy użytkownik jawnie wyraził zgodę na przechwytywanie okna aplikacji lub wyświetlania w interfejsie użytkownika systemu, GraphicsCaptureItem może być skojarzony z wieloma obiektami CaptureSession. Dzięki temu aplikacja może zdecydować o przechwytywaniu tego samego elementu dla różnych doświadczeń.

Aby przechwycić wiele elementów jednocześnie, aplikacja musi utworzyć sesję przechwytywania dla każdego elementu do przechwycenia, co wymaga wywołania interfejsu użytkownika selektora dla każdego elementu, który ma zostać przechwycony.

Uzyskiwanie ramek przechwytywania

Po utworzeniu puli ramek i sesji przechwytywania wywołaj metodę StartCapture w wystąpieniu GraphicsCaptureSession, aby powiadomić system o rozpoczęciu wysyłania ramek przechwytywania do aplikacji:

_session.StartCapture();
_session.StartCapture()

Aby uzyskać te ramki przechwytywania, które są obiektami Direct3D11CaptureFrame, można użyć zdarzenia Direct3D11CaptureFramePool.FrameArrived:

_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

Zaleca się unikanie korzystania z wątku interfejsu użytkownika, jeśli jest to możliwe w przypadku FrameArrived, ponieważ to zdarzenie będzie zgłaszane za każdym razem, gdy jest dostępna nowa ramka, co będzie częste. Jeśli zdecydujesz się słuchać FrameArrived w wątku interfejsu użytkownika, należy pamiętać o tym, ile pracy wykonujesz za każdym razem, gdy zdarzenie jest uruchamiane.

Alternatywnie możesz ręcznie ściągnąć ramki za pomocą metody Direct3D11CaptureFramePool.TryGetNextFrame, dopóki nie uzyskasz wszystkich potrzebnych ramek.

Obiekt Direct3D11CaptureFrame zawiera właściwości ContentSize, Surfacei SystemRelativeTime. SystemRelativeTime to czas QPC (QueryPerformanceCounter), który może służyć do synchronizowania innych elementów multimedialnych.

Przetwarzanie klatek przechwyconych obrazów

Każda ramka z Direct3D11CaptureFramePool jest wyewidencjonowana podczas wywoływania TryGetNextFramei zaewidencjonowana z powrotem zgodnie z okresem istnienia obiektu Direct3D11CaptureFrame. W przypadku aplikacji natywnych wystarczy zwolnić obiekt Direct3D11CaptureFrame, by móc ponownie dodać klatkę do puli klatek. W przypadku aplikacji zarządzanych zaleca się użycie metody Direct3D11CaptureFrame.Dispose (Close w języku C++). Direct3D11CaptureFrame implementuje interfejs IClosable, który jest odwzorowany jako IDisposable dla wywołujących w C#.

Aplikacje nie powinny zapisywać odwołań do obiektów Direct3D11CaptureFrame oraz do podstawowej powierzchni Direct3D po ponownym zwróceniu ramki.

Podczas przetwarzania ramki zaleca się, żeby aplikacje przyjęły blokadę ID3D11Multithread na tym samym urządzeniu skojarzonym z obiektem Direct3D11CaptureFramePool.

Podstawowa powierzchnia Direct3D będzie zawsze miała rozmiar określony podczas tworzenia (lub ponownego tworzenia) Direct3D11CaptureFramePool. Jeśli zawartość jest większa niż ramka, zawartość zostanie obcięta do rozmiaru ramki. Jeśli zawartość jest mniejsza niż ramka, pozostała część ramki zawiera niezdefiniowane dane. Zaleca się, aby aplikacje kopiowały podprostokąt przy użyciu właściwości ContentSize dla tej Direct3D11CaptureFrame, w celu uniknięcia wyświetlania niezdefiniowanej zawartości.

Wykonaj zrzut ekranu

W naszym przykładzie konwertujemy każdą Direct3D11CaptureFrame na CanvasBitmap, która jest częścią API Win2D.

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

Gdy mamy CanvasBitmap, możemy zapisać go jako plik obrazu. W poniższym przykładzie zapiszemy go jako plik PNG w folderze Saved Pictures użytkownika.

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

Reagowanie na zmianę rozmiaru elementu lub utratę urządzenia

Podczas procesu przechwytywania, aplikacje mogą chcieć zmienić aspekty swojego Direct3D11CaptureFramePool. Obejmuje to udostępnienie nowego urządzenia Direct3D, zmianę rozmiaru buforów ramki, a nawet zmianę liczby buforów w puli. W każdym z tych scenariuszy metodą zalecaną jest użycie Recreate na obiekcie Direct3D11CaptureFramePool.

Gdy wywołane zostanie Recreate, wszystkie istniejące ramki zostaną odrzucone. Zapobiega to rozdaniu ramek, których bazowe powierzchnie Direct3D należą do urządzenia, do którego aplikacja może już nie mieć dostępu. Z tego powodu może być rozsądne przetworzyć wszystkie oczekujące ramki przed wywołaniem Utwórz ponownie.

Łączenie tego wszystkiego

Poniższy fragment kodu to pełny przykład implementacji przechwytywania ekranu w aplikacji platformy UWP. W tym przykładzie mamy dwa przyciski w frontonie: jedno wywołuje Button_ClickAsync, a drugie wywołuje ScreenshotButton_ClickAsync.

Uwaga / Notatka

Ten fragment kodu używa Win2D , biblioteki do renderowania grafiki 2D. Zapoznaj się z ich dokumentacją, aby uzyskać informacje o sposobie konfigurowania go dla projektu.

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

Nagrywanie wideo

Jeśli chcesz nagrać wideo z działania swojej aplikacji, możesz skorzystać z instrukcji przedstawionej w artykule Przechwytywanie ekranu do wideo. Możesz też użyć przestrzeni nazw Windows.Media.AppRecording. Jest to część zestawu SDK rozszerzenia pulpitu, więc działa tylko na komputerach z systemem Windows i wymaga dodania odwołania do niego z projektu. Aby uzyskać więcej informacji, zobacz Programowanie z zestawami SDK rozszerzeń.

Zobacz także