Tangkapan layar

Mulai dari Windows 10, versi 1803, namespace Windows.Graphics.Capture menyediakan API untuk memperoleh bingkai dari jendela tampilan atau aplikasi, untuk membuat aliran video atau rekam jepret untuk membangun pengalaman kolaboratif dan interaktif.

Dengan tangkapan layar, pengembang memanggil UI sistem aman bagi pengguna akhir untuk memilih tampilan atau jendela aplikasi yang akan diambil, dan batas pemberitahuan kuning digambar oleh sistem di sekitar item yang diambil secara aktif. Dalam kasus beberapa sesi pengambilan simultan, batas kuning digambar di sekitar setiap item yang diambil.

Catatan

API tangkapan layar hanya didukung pada perangkat Windows dan headset imersif Windows Mixed Reality.

Artikel ini menjelaskan menangkap satu gambar jendela tampilan atau aplikasi. Untuk informasi tentang bingkai pengodean yang diambil dari layar ke file video, lihat Tangkapan layar ke video

Menambahkan kemampuan tangkapan layar

API yang ditemukan di namespace Windows.Graphics.Capture memerlukan kemampuan umum untuk dideklarasikan dalam manifes aplikasi Anda:

  1. Buka Package.appxmanifest di Penjelajah Solusi.
  2. Pilih tab Kemampuan.
  3. Periksa Pengambilan Grafis.

Graphics Capture

Luncurkan antarmuka pengguna sistem untuk memulai pengambilan layar

Sebelum meluncurkan antarmuka pengguna sistem, Anda dapat memeriksa untuk melihat apakah aplikasi Anda saat ini dapat mengambil tangkapan layar. Ada beberapa alasan mengapa aplikasi Anda mungkin tidak dapat menggunakan tangkapan layar, termasuk jika perangkat tidak memenuhi persyaratan perangkat keras atau jika aplikasi yang ditargetkan untuk pengambilan layar blok pengambilan. Gunakan metode IsSupported di kelas GraphicsCaptureSession untuk menentukan apakah tangkapan layar UWP didukung:

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

Setelah Anda memverifikasi bahwa tangkapan layar didukung, gunakan kelas GraphicsCapturePicker untuk memanggil UI pemilih sistem. Pengguna akhir menggunakan UI ini untuk memilih jendela tampilan atau aplikasi yang akan diambil tangkapan layarnya. Pemilih akan mengembalikan GraphicsCaptureItem yang akan digunakan untuk membuat 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

Karena ini adalah kode UI, kode tersebut perlu dipanggil pada utas UI. Jika Anda memanggilnya dari code-behind untuk halaman aplikasi Anda (seperti MainPage.xaml.cs) ini dilakukan untuk Anda secara otomatis, tetapi jika tidak, Anda dapat memaksanya untuk berjalan di utas UI dengan kode berikut:

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

Membuat kumpulan bingkai pengambilan dan mengambil sesi

Dengan menggunakan GraphicsCaptureItem, Anda akan membuat Direct3D11CaptureFramePool dengan perangkat D3D Anda, format piksel yang didukung (DXGI_FORMAT_B8G8R8A8_UNORM), jumlah bingkai yang diinginkan (yang dapat berupa bilangan bulat apa pun), dan ukuran bingkai. Properti ContentSize dari kelas GraphicsCaptureItem dapat digunakan sebagai ukuran bingkai Anda:

Catatan

Pada sistem dengan warna HD Windows diaktifkan, format piksel konten mungkin belum tentu DXGI_FORMAT_B8G8R8A8_UNORM. Untuk menghindari overkliping piksel (yaitu konten yang diambil terlihat dicuci) saat menangkap konten HDR, pertimbangkan untuk menggunakan DXGI_FORMAT_R16G16B16A16_FLOAT untuk setiap komponen dalam alur penangkapan, termasuk Direct3D11CaptureFramePool, tujuan target seperti CanvasBitmap. Tergantung pada kebutuhan, pemrosesan tambahan seperti menyimpan ke format konten HDR atau pemetaan nada HDR-ke-SDR mungkin diperlukan. Artikel ini akan berfokus pada pengambilan konten SDR. Untuk informasi selengkapnya, silakan lihat Menggunakan DirectX dengan Tampilan rentang dinamis tinggi dan Warna Tingkat Lanjut.

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

Selanjutnya, dapatkan instans kelas GraphicsCaptureSession untuk Direct3D11CaptureFramePool Anda dengan meneruskan GraphicsCaptureItem ke metode CreateCaptureSession:

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

Setelah pengguna secara eksplisit memberikan persetujuan untuk menangkap jendela aplikasi atau tampilan di antarmuka pengguna sistem, GraphicsCaptureItem dapat dikaitkan dengan beberapa objek CaptureSession . Dengan cara ini aplikasi Anda dapat memilih untuk mengambil item yang sama untuk berbagai pengalaman.

Untuk mengambil beberapa item secara bersamaan, aplikasi Anda harus membuat sesi pengambilan untuk setiap item yang akan diambil, yang mengharuskan memanggil UI pemilih untuk setiap item yang akan diambil.

Memperoleh bingkai pengambilan

Dengan kumpulan bingkai dan sesi pengambilan Anda dibuat, panggil metode StartCapture pada instans GraphicsCaptureSession Anda untuk memberi tahu sistem untuk mulai mengirim bingkai pengambilan ke aplikasi Anda:

_session.StartCapture();
_session.StartCapture()

Untuk memperoleh bingkai pengambilan ini, yang merupakan objek Direct3D11CaptureFrame , Anda dapat menggunakan peristiwa 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

Disarankan untuk menghindari penggunaan utas UI jika memungkinkan untuk FrameArrived, karena peristiwa ini akan dinaikkan setiap kali bingkai baru tersedia, yang akan sering terjadi. Jika Anda memilih untuk mendengarkan FrameArrived di utas UI, perhatikan berapa banyak pekerjaan yang Anda lakukan setiap kali peristiwa diaktifkan.

Atau, Anda dapat menarik bingkai secara manual dengan metode Direct3D11CaptureFramePool.TryGetNextFrame hingga Anda mendapatkan semua bingkai yang Anda butuhkan.

Objek Direct3D11CaptureFrame berisi properti ContentSize, Surface, dan SystemRelativeTime. SystemRelativeTime adalah waktu QPC (QueryPerformanceCounter) yang dapat digunakan untuk menyinkronkan elemen media lainnya.

Bingkai pengambilan proses

Setiap bingkai dari Direct3D11CaptureFramePool dicek keluar saat memanggil TryGetNextFrame, dan diperiksa kembali sesuai dengan masa pakai objek Direct3D11CaptureFrame. Untuk aplikasi asli, merilis objek Direct3D11CaptureFrame sudah cukup untuk memeriksa bingkai kembali ke kumpulan bingkai. Untuk aplikasi terkelola, disarankan untuk menggunakan metode Direct3D11CaptureFrame.Dispose (Tutup dalam C++). Direct3D11CaptureFrame mengimplementasikan antarmuka IClosable , yang diproyeksikan sebagai IDisposable untuk pemanggil C#.

Aplikasi tidak boleh menyimpan referensi ke objek Direct3D11CaptureFrame , juga tidak boleh menyimpan referensi ke permukaan Direct3D yang mendasarinya setelah bingkai diperiksa kembali.

Saat memproses bingkai, disarankan agar aplikasi mengambil kunci ID3D11Multithread pada perangkat yang sama yang terkait dengan objek Direct3D11CaptureFramePool .

Permukaan Direct3D yang mendasarinya akan selalu menjadi ukuran yang ditentukan saat membuat (atau membuat ulang) Direct3D11CaptureFramePool. Jika konten lebih besar dari bingkai, konten akan diklip ke ukuran bingkai. Jika konten lebih kecil dari bingkai, bingkai lainnya berisi data yang tidak ditentukan. Disarankan agar aplikasi menyalin sub-rect menggunakan properti ContentSize untuk Direct3D11CaptureFrame tersebut untuk menghindari menampilkan konten yang tidak ditentukan.

Ambil cuplikan layar

Dalam contoh kami, kami mengonversi setiap Direct3D11CaptureFrame menjadi CanvasBitmap, yang merupakan bagian dari API Win2D.

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

Setelah kita memiliki CanvasBitmap, kita dapat menyimpannya sebagai file gambar. Dalam contoh berikut, kami menyimpannya sebagai file PNG di folder Gambar Tersimpan pengguna.

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

Bereaksi terhadap mengubah ukuran item atau perangkat yang hilang

Selama proses pengambilan, aplikasi mungkin ingin mengubah aspek Direct3D11CaptureFramePool mereka. Ini termasuk menyediakan perangkat Direct3D baru, mengubah ukuran buffer bingkai, atau bahkan mengubah jumlah buffer dalam kumpulan. Dalam setiap skenario ini, metode Buat ulang pada objek Direct3D11CaptureFramePool adalah alat yang direkomendasikan.

Ketika Dibuat ulang dipanggil, semua bingkai yang ada akan dibuang. Ini untuk mencegah membagikan bingkai yang permukaan Direct3D yang mendasarnya milik perangkat yang mungkin tidak lagi dapat diakses oleh aplikasi. Untuk alasan ini, mungkin bijaksana untuk memproses semua bingkai yang tertunda sebelum memanggil Buat Ulang.

Merangkum semuanya

Cuplikan kode berikut adalah contoh end-to-end tentang cara mengimplementasikan tangkapan layar dalam aplikasi UWP. Dalam sampel ini, kita memiliki dua tombol di front-end: satu panggilan Button_ClickAsync, dan panggilan lainnya ScreenshotButton_ClickAsync.

Catatan

Cuplikan ini menggunakan Win2D, pustaka untuk penyajian grafis 2D. Lihat dokumentasi mereka untuk informasi tentang cara menyiapkannya untuk proyek Anda.

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

Merekam video

Jika Anda ingin merekam video aplikasi, Anda dapat mengikuti panduan yang disajikan dalam artikel Tangkapan layar ke video. Atau, Anda dapat menggunakan namespace Layanan Windows.Media.AppRecording. Ini adalah bagian dari SDK ekstensi Desktop, sehingga hanya berfungsi pada desktop Windows dan mengharuskan Anda menambahkan referensi ke dalamnya dari proyek Anda. Lihat Pemrograman dengan SDK ekstensi untuk informasi selengkapnya.

Baca juga