在 C# 中固定对象时的意外行为

Unexpected behaviour when pinning object in c#

当我执行此代码时,我正在使用 C# 的 ImageSharp 库

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Advanced;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace ImageSharp
{
    public class Program
    {
        public static void Main()
        {
            Image<Rgba32> img = null;
            using (var imageFileStream = new FileStream(/*Any jpg image.*/@"E:\cat[=10=].jpg", FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                img = Image.Load(imageFileStream);
            }
            int length = img.Height * img.Width / 2;
            //Rgba32[] colors = typeof(Rgba32).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public).Where(a => a.FieldType == typeof(Rgba32)).Select(a => (Rgba32)a.GetValue(null)).ToArray();
            Span<Rgba32> buffer = Span<Rgba32>.Empty;
            GCHandle bufferHandle = GCHandle.Alloc(img.DangerousGetPinnableReferenceToPixelBuffer(), GCHandleType.Pinned);
            unsafe
            {
                buffer = new Span<Rgba32>(bufferHandle.AddrOfPinnedObject().ToPointer(), length);
            }
            for (int i = 0; i < length; i++)
            {
                buffer[i] = Rgba32.Yellow;
                Console.WriteLine(i);//exception thrown here
            }
            buffer = Span<Rgba32>.Empty;
            bufferHandle.Free();
            GC.Collect();
            using (var imageFileStream = new FileStream(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), @"ImageSharp.jpg"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read))
            {
                img.SaveAsJpeg(imageFileStream);
                imageFileStream.Flush();
            }
            Console.WriteLine("Done!");
            Console.ReadLine();
        }
    }
}

我遇到了这个异常(System.IO.IOException:'The handle is invalid.')。 如果你删除抛出异常的行,程序就会挂起(我认为它会挂在循环内)。

所以我的问题是导致此异常的原因以及为什么当您删除 "Console.WriteLine" 程序会挂起?

项目中唯一的依赖是ImageSharp nuget包。 框架版本:4.7.1

您似乎在尝试固定缓冲区,然后使用 Span<T>

遍历它

你错了。

GCHandle.Alloc(img.DangerousGetPinnableReferenceToPixelBuffer(), GCHandleType.Pinned);

您正在对表示图像第一个像素的结构进行装箱,并为该装箱对象实例创建 GCHandle

如果您真的需要固定缓冲区,我建议您改为这样做。

fixed (Rgba32* ptr = &img.DangerousGetPinnableReferenceToPixelBuffer())
{
    Console.WriteLine(ptr[42]); // no Span<T> needed!
}

但是...我真的不明白你为什么要这样做。使用 Image<TPixel>[x,y] 索引器和可用于 Fill 像素区域的方法,已经可以通过 API 进行逐像素访问。我强烈建议您使用可用的方法。