.NET 5环境下二次克隆静态System.Drawing.Bitmap出现空引用或无效值异常
GDI+ Bitmap二次克隆抛出Wrong status异常的原因与解决方案
问题背景
基于.NET 5.0、C#开发的胶片帧数字化程序,使用Accord.Video组件通过USB显微镜采集到的System.Drawing.Bitmap存储为静态变量后,首次克隆正常,第二次克隆抛出异常:
Object is not in a state that can allow this operation (GDI+ status Wrong status)
经排查异常由Palette属性异常引发。
引发原因
- GDI+对象生命周期限制:
System.Drawing.Bitmap底层依赖GDI+非托管资源,当位图被锁定(像素操作、GDI上下文持有、资源被部分回收)时,内部状态会被标记为非法,Wrong status是GDI+对象状态无效的典型错误码。 - 静态存储的状态污染:存储为静态变量的原始采集位图,首次克隆或处理过程中如果隐式修改了位图元数据、持有了GDI资源引用未释放,会导致调色板等核心属性被破坏,第二次操作时无法读取合法元数据。
- 索引像素格式的特殊限制:Accord采集返回的位图多为索引像素格式,其Palette属性属于GDI+托管的特殊资源,一旦被意外释放或修改,就会导致克隆操作校验位图格式失败。
解决方案
1. 采集后立即做深度拷贝,不存储原始采集位图
采集得到原始Bitmap后,立即新建独立的Bitmap副本用于静态存储,切断与Accord组件持有的原始位图的资源关联,避免Accord内部资源回收影响存储的位图状态,示例代码:
// 从Accord采集得到原始位图originalBmp Bitmap storedBmp = new Bitmap(originalBmp.Width, originalBmp.Height, originalBmp.PixelFormat); using (Graphics g = Graphics.FromImage(storedBmp)) { g.DrawImage(originalBmp, new Rectangle(0, 0, storedBmp.Width, storedBmp.Height)); } // 索引格式需单独复制调色板 if (originalBmp.PixelFormat.HasFlag(PixelFormat.Indexed)) { storedBmp.Palette = originalBmp.Palette; } // 释放原始采集位图 originalBmp.Dispose(); // 将storedBmp作为静态存储对象使用
2. 改用像素复制方式生成克隆,不调用原生Clone()方法
Bitmap.Clone()为浅拷贝,会共享底层GDI+资源,容易触发状态冲突,改为手动复制像素数据生成独立副本:
public Bitmap CreateBitmapClone(Bitmap source) { Bitmap clone = new Bitmap(source.Width, source.Height, source.PixelFormat); // 锁定源位图读取像素 BitmapData sourceData = source.LockBits( new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, source.PixelFormat); // 锁定目标位图写入像素 BitmapData cloneData = clone.LockBits( new Rectangle(0, 0, clone.Width, clone.Height), ImageLockMode.WriteOnly, clone.PixelFormat); // 复制全量像素数据 unsafe { Buffer.MemoryCopy( sourceData.Scan0.ToPointer(), cloneData.Scan0.ToPointer(), cloneData.Stride * cloneData.Height, cloneData.Stride * cloneData.Height); } // 解除锁定 source.UnlockBits(sourceData); clone.UnlockBits(cloneData); // 复制调色板 if (source.PixelFormat.HasFlag(PixelFormat.Indexed)) { clone.Palette = source.Palette; } return clone; }
3. 禁止直接修改静态存储的位图
所有处理操作仅在克隆生成的副本上执行,不要直接修改静态存储位图的属性、像素数据,确保静态位图的GDI+状态始终合法。
内容的提问来源于stack exchange,提问作者Wolfgang Kurz
相关产品推荐
相关产品推荐

