C#/UWP 中的屏幕捕获

Screen capturing in C#/UWP

我正在使用 UWP 桌面 window 应用程序,我正在尝试实现截取屏幕截图并将其保存到本地文件的功能,但我收到错误消息:
System.NullReferenceException: '对象引用未设置为对象的实例。
在等待 frame.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);在 SaveImageAsync 方法中。

你能告诉我哪里出了问题吗?

namespace App5
{
public sealed partial class MainPage : Page

{

    private SizeInt32 _lastSize;
    private GraphicsCaptureItem _item;
    private Direct3D11CaptureFramePool _framePool;
    private GraphicsCaptureSession _session;

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

        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()
    {
        var picker = new GraphicsCapturePicker();
        GraphicsCaptureItem item = await picker.PickSingleItemAsync();

        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) =>
        {

            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)
    {
        bool needsReset = false;
        bool recreateDevice = false;

        if ((frame.ContentSize.Width != _lastSize.Width) ||
            (frame.ContentSize.Height != _lastSize.Height))
        {
            needsReset = true;
            _lastSize = frame.ContentSize;
        }

        try
        {
            CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromDirect3D11Surface(
                _canvasDevice,
                frame.Surface);

            _currentFrame = canvasBitmap;

            FillSurfaceWithBitmap(canvasBitmap);
        }

        catch (Exception e) when (_canvasDevice.IsDeviceLost(e.HResult))
        {

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

}

此代码来自 Windows Forms .Net Core 项目,但我只是粘贴到 WPF 项目中并且它有效。

在 WPF 中你必须添加 Nuget 包 System.Drawing.Common:

您还必须添加对以下内容的引用:

using System.Windows.Forms;
using WindowsFormsIntegration;
using System.Drawing;

// take a screenshot
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);

// to save:
bitmap.Save(fileName);

以上代码来自本项目。我帮助某人创建子图像并在更大的图像中搜索子图像。他们有截图代码。

https://github.com/DataJuggler/SubImageCreator

本项目用于从大图创建子图,并在大图中搜索子图。