自定义只读Stream类调用BitmapFrame.Create时偶发SetLength异常求助
这个问题我之前也碰到过,本质是BitmapFrame.Create在特定处理路径下,即使你传入的是只读流,也可能尝试调用SetLength方法——尤其是当你使用BitmapCacheOption.None时,因为这个选项会让WPF直接操作原始流而不缓存数据,不同的图像格式、流的位置或者内部处理逻辑的随机性,导致了40%的触发概率。
下面给你几个可行的解决方案,按推荐优先级排序:
1. 修改BitmapCacheOption为OnLoad
最简单的办法是把缓存选项改成BitmapCacheOption.OnLoad,这个选项会让WPF在创建BitmapFrame时就把整个图像数据加载到内存中,之后就不会再对原始流进行任何操作,自然也就不会触发SetLength了:
var frame = BitmapFrame.Create(stream, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnLoad);
这个方案不需要修改你的自定义Stream类,对内存的占用和直接操作原始流差不多(因为数据还是要加载),但能彻底避免流操作的问题。
2. 重写SetLength方法适配只读场景
如果你必须使用BitmapCacheOption.None,可以修改你自定义Stream的SetLength实现,不要直接抛出自定义异常,而是遵循.NET标准的只读流行为:要么抛出框架内置的NotSupportedException(框架代码通常能正确处理这个异常),要么在设置长度与当前一致时静默忽略:
public override void SetLength(long value) { // 先检查是否是设置当前长度,是的话直接返回 if (value == this.Length) { return; } // 否则抛出标准的不支持异常 throw new NotSupportedException("This stream is read-only and does not support setting length."); }
这样即使BitmapFrame.Create调用了SetLength,只要符合只读流的预期行为,框架大概率会跳过后续的修改操作。
3. 用MemoryStream包装自定义流
如果上面两个方案都不适用,你可以先把自定义流的数据复制到一个MemoryStream中,再用这个内存流来创建BitmapFrame——因为MemoryStream完全支持SetLength等所有流操作,不会触发异常:
using var memoryStream = new MemoryStream(); yourCustomStream.CopyTo(memoryStream); memoryStream.Position = 0; // 重置流位置到开头 var frame = BitmapFrame.Create(memoryStream, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.None);
这个方案的缺点是需要把整个流数据加载到内存,对于大图像来说会占用较多内存,但胜在兼容性最好,不管BitmapFrame.Create做什么操作都不会有问题。
内容的提问来源于stack exchange,提问作者user3144320

