WPF 已知问题 BitmapDecoder.Create 不支持传入 Asynchronous 的文件流
这是在 GitHub 上有小伙伴报的问题,在 WPF 中,不支持调用 BitmapDecoder.Create 方法,传入的 FileStream 是配置了 FileOptions.Asynchronous 选项的文件流。本质原因是 WIC 层不支持,和 WPF 没有关系
现象是传入 BitmapDecoder.Create 的 FileStream 配置了 FileOptions.Asynchronous 选项,代码如下
using var fs = new FileStream("image.jpg",
FileMode.Open,
FileAccess.Read,
FileShare.Read,
4096,
FileOptions.Asynchronous);
var decoder = BitmapDecoder.Create(fs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
运行以上代码将会抛出 ArgumentException 而创建解码器失败
本质原因是 WIC 层不支持 FileStream 配置了 FileOptions.Asynchronous 选项。在 BitmapDecoder.Create 的底层,调用了 IWICImagingFactory_CreateDecoderFromFileHandle_Proxy function - Win32 apps 方法创建解码器,代码如下
public static BitmapDecoder Create(
Stream bitmapStream,
BitmapCreateOptions createOptions,
BitmapCacheOption cacheOption
)
{
// 忽略代码
return CreateFromUriOrStream(
null,
null,
bitmapStream,
createOptions,
cacheOption,
null,
true
);
}
internal static BitmapDecoder CreateFromUriOrStream(
Uri baseUri,
Uri uri,
Stream stream,
BitmapCreateOptions createOptions,
BitmapCacheOption cacheOption,
RequestCachePolicy uriCachePolicy,
bool insertInDecoderCache
)
{
// 忽略代码
decoderHandle = BitmapDecoder.SetupDecoderFromUriOrStream(
finalUri,
stream,
cacheOption,
out clsId,
out isOriginalWritable,
out uriStream,
out unmanagedMemoryStream,
out safeFilehandle
);
// 忽略代码
}
internal static SafeMILHandle SetupDecoderFromUriOrStream(
Uri uri,
Stream stream,
BitmapCacheOption cacheOption,
out Guid clsId,
out bool isOriginalWritable,
out Stream uriStream,
out UnmanagedMemoryStream unmanagedMemoryStream,
out SafeFileHandle safeFilehandle
)
{
using (FactoryMaker myFactory = new FactoryMaker())
{
HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateDecoderFromFileHandle(
myFactory.ImagingFactoryPtr,
safeFilehandle,
ref vendorMicrosoft,
metadataFlags,
out decoder
));
}
}
也就是说最底层调用就是通过 CreateDecoderFromFileHandle 方法创建解码器,而 CreateDecoderFromFileHandle 的定义如下
internal static class WICImagingFactory
{
[DllImport(DllImport.WindowsCodecs, EntryPoint = "IWICImagingFactory_CreateDecoderFromFileHandle_Proxy")]
internal static extern int /*HRESULT*/ CreateDecoderFromFileHandle(
IntPtr pICodecFactory,
Microsoft.Win32.SafeHandles.SafeFileHandle /*ULONG_PTR*/ hFileHandle,
ref Guid guidVendor,
UInt32 metadataFlags,
out IntPtr /* IWICBitmapDecoder */ ppIDecode);
}
从以上代码可以看到是在 WindowsCodecs.dll 也就是 WIC 层的 IWICImagingFactory_CreateDecoderFromFileHandle_Proxy 抛出失败的
在 FileStream 创建中,如果传入了 FileOptions.Asynchronous 参数,将会在 Windows 下,调用的是 CreateFileW function (fileapi.h) - Win32 apps 方法,在这个方法里面将会设置 FILE_FLAG_OVERLAPPED 参数。不过我没有从网上找到在设置了 FILE_FLAG_OVERLAPPED 参数的文件句柄将在 IWICImagingFactory_CreateDecoderFromFileHandle_Proxy 或 IWICImagingFactory::CreateDecoderFromFileHandle 方法不支持的知识
我写了一个简单的 demo 程序,用来测试是否 FileOptions.Asynchronous 参数的文件句柄将会在 WIC 层不支持
class Program
{
static void Main(string[] args)
{
CheckHResult(UnsafeNativeMethods.WICCodec.CreateImagingFactory(UnsafeNativeMethods.WICCodec.WINCODEC_SDK_VERSION,
out var pImagingFactory));
using var fs = new FileStream("image.jpg",
FileMode.Open,
FileAccess.Read,
FileShare.Read,
4096,
FileOptions.Asynchronous);
Guid vendorMicrosoft = new Guid(MILGuidData.GUID_VendorMicrosoft);
UInt32 metadataFlags = (uint)WICMetadataCacheOptions.WICMetadataCacheOnDemand;
CheckHResult
(
UnsafeNativeMethods.WICImagingFactory.CreateDecoderFromFileHandle
(
pImagingFactory,
fs.SafeFileHandle,
ref vendorMicrosoft,
metadataFlags,
out var decoder
)
);
}
static void CheckHResult(int hr)
{
if (hr < 0)
{
Exception exceptionForHR = Marshal.GetExceptionForHR(hr, (IntPtr)(-1));
throw exceptionForHR;
}
}
}
通过以上代码可以看到,如果 FileStream 的创建参数里面加上了 FileOptions.Asynchronous 那么将会在 CreateDecoderFromFileHandle 抛出错误
因此在 WPF 中,调用 BitmapDecoder.Create 方法,传入的带 FileOptions.Asynchronous 的 FileStream 抛出错误,不是 WPF 层的锅,而是 WIC 层不支持。在 GitHub 上报告的作者 Nikita Kazmin 给了一个我同意的建议是 WPF 在 BitmapDecoder.Create 方法里面应该判断一下,如果传入的 FileStream 是异步的,那么在 WPF 层抛出错误,这样方便开发者了解不能这样使用
我也有另一个想法,如果是 FileStream 是异步的,不如完全读取到内存里面,这样开发者也就可以不关注这部分的逻辑。我当前将此逻辑放入到 WPF 仓库中,详细请看 https://github.com/dotnet/wpf/pull/4966
本文所有代码放在 github 和 gitee 欢迎小伙伴访问
当前的 WPF 在 https://github.com/dotnet/wpf 完全开源,使用友好的 MIT 协议,意味着允许任何人任何组织和企业任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。在仓库里面包含了完全的构建逻辑,只需要本地的网络足够好(因为需要下载一堆构建工具),即可进行本地构建
WPF 已知问题 BitmapDecoder.Create 不支持传入 Asynchronous 的文件流的更多相关文章
- go-carbon 1.5.2版本发布, 修复已知 bug 和新增功能及葡萄牙语翻译文件
carbon 是一个轻量级.语义化.对开发者友好的golang时间处理库,支持链式调用. 目前已被 [awesome-go](https://github.com/avelino/awesome-go ...
- Confluence 6 指定日志选项和已知问题
指定 Confluence 日志选项 这里是一些特定的日志配置,你可能在对问题进行调试的时候需要. 在日志中记录数据库使用的 SQL 查询请求 你可能希望增加日志的中的内容,记录 Confluence ...
- Python实现加密的ZIP文件解压(密码已知)
博主在上篇博文介绍了<Python实现加密的RAR文件解压(密码已知)>后,又尝试了ZIP文件的解压方法,下面开始分享. 当ZIP文件的压缩密码已知时,可以通过调用zipfile库进行解压 ...
- [推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼、百战不殆)
原文:[推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼.百战不殆) [推荐]ORACLE PL/SQL编程之五: 异常错误处理(知已知彼.百战不殆) 继上三篇:ORACLE PL/S ...
- python预科前三天:计算器知识、Python下载和安装、Pycharm下载安装激活设置、解释型和编译型、git、思维导图、显示隐藏文件、隐藏已知文件扩展名、创建组织、创建项目、提交作业、排BUG技巧
1.计算机组成结构:CPU.硬盘.内存.输入输出设备.主板.电源. 2.硬件之间的协作关系:是CPU运算完后给操作系统.专业术语叫指令. 3.键盘输入a之后发生的事情:键盘-CPU-操作系统-显卡-显 ...
- WCF 已知类型和泛型解析程序 KnownType
数据协定继承 已知类型和泛型解析程序 Juval Lowy 下载代码示例 自首次发布以来,Windows Communication Foundation (WCF) 开发人员便必须处理数据协定继承方 ...
- 已知json类型根据类型封装集合
1编写帮助类根绝url得到json public static string Post(string url) { string strURL = url; //创建一个HTTP请求 HttpWebR ...
- 剑指offer——已知二叉树的先序和中序排列,重构二叉树
这是剑指offer中关于二叉树重构的一道题.题目原型为: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2, ...
- WP8.1开发者预览版本号已知 Bug
偶的 Lumia 920 已经升级到最新的 8.1 开发者预览版本号,使用中没有发现什么问题. 可能是由于偶玩手机的情况比較少吧!忽然看到 MS 停止此版本号的更新,并说明有非常多的 BUG,偶就郁闷 ...
- EF对于已有数据库的Code First支持
EF对于已有数据库的Code First支持 原文链接 本文将逐步介绍怎样用Code First的方式基于已有数据库进行开发.Code First支持你使用C#或者VB.Net定义类.并使用数据模型标 ...
随机推荐
- 你是怎么理解ES6中 Promise的?使用场景?
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.介绍 Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大 在以往我们如果处理多层异步操 ...
- 记录--两行CSS让页面提升了近7倍渲染性能!
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 对于前端人员来讲,最令人头疼的应该就是页面性能了,当用户在访问一个页面时,总是希望它能够快速呈现在眼前并且是可交互状态.如果页面加载 ...
- 美团一面:说一说Java中的四种引用类型?
引言 在JDK1.2之前Java并没有提供软引用.弱引用和虚引用这些高级的引用类型.而是提供了一种基本的引用类型,称为Reference.并且当时Java中的对象只有两种状态:被引用和未被引用.当一个 ...
- docker-compose转义相关
环境变量值里面写$美元符号,用两个$符号来转义就可以了 下面的MYSQL_ROOT_PASSWORD的密码是lehuiguan!@#$,转义后写的变量就是lehuiguan!@#$$ environm ...
- YOLACT++ : 实时实例分割,从29.8mAP/33.5fps到34.1mAP/33.5fps
YOLACT是首个实时实例分割算法,但是准确率较SOTA差得有点多,YOLACT++从主干网络.分支和anchor的3个角度出发对YOLACT进行优化,在保持实时性的前提下提升了5map,论文改进的角 ...
- KingbaseESV8R6普通用户无权限执行vacuum
背景 数据库日志有如下提示: WARNING: skipping "pivot_t1" --- only table or database owner can vacuum it ...
- 学习Python前要了解的tips
学习Python前要了解的tips 对后续的学习来说很重要,否则后续会出现一些奇奇怪怪的问题,而且很难找到解决方法.嘿嘿,就不要问我怎么知道的了吧,好多都是我踩过的坑 卸载电脑内软件 之前我一直用电脑 ...
- Make It Equal 题解
Problem Link 简要题意 翻译很清楚. 思路 提供一种简单直接的思路. 可以发现最多会操作 \(n\) 次. 那么就可以每次直接枚举切的高度 \(h\),检查更改是否超过 \(k\),之后暴 ...
- #分块,可撤销并查集#洛谷 5443 [APIO2019]桥梁
题目 分析 最直接的做法就是在线一边修改边权,询问直接全部重排, 然后用可撤销并查集维护连通块大小,这样时间复杂度为 \(O(qm)\) 同样尽量让大部分的边不需要修改边权,那么每 \(B\) 个操作 ...
- JDK 14的新特性:switch表达式
目录 简介 写在前面 连写case switch返回值 yield 总结 简介 switch的新特性可是源远流长,早在JDK 12就以预览功能被引入了,最终在JDK 14成为了正式版本的功能:JEP ...