首页 > 解决方案 > C#/UWP 中的屏幕截图

问题描述

我正在使用 UWP 桌面窗口应用程序,我正在尝试实现将屏幕截图保存到本地文件的功能,但我收到错误消息:
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);
        }
    }
}

}

标签: c#uwp-xaml

解决方案


此代码来自 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

该项目用于从较大的图像创建子图像并在较大的图像中搜索子图像。


推荐阅读