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定义类.并使用数据模型标 ...
随机推荐
- 记录--前端实习生的这个 bug 被用做了一道基础面试题
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 测试发现了一个问题,简单描述问题就是通过函数删除一个数组中多个元素,传入的参数是一个数组索引. 然后发现实际效果有时删除的不是想要的 ...
- 开发进阶系列:Java并发之从基础到框架
一 线程基础 1.synchronized取得的锁都是对象锁,哪个线程执行synchronized修饰的方法,哪个线程就获得这个方法所属对象的锁.不同对象不同锁,互不影响. 另一种情况是static ...
- CentOS 安装webmin
下载地址 http://download.webmin.com/download/yum/ 安装依赖 sudo yum -y install openssl perl perl-Net-SSLeay ...
- Dynamic ReLU:微软推出提点神器,可能是最好的ReLU改进 | ECCV 2020
论文提出了动态ReLU,能够根据输入动态地调整对应的分段激活函数,与ReLU及其变种对比,仅需额外的少量计算即可带来大幅的性能提升,能无缝嵌入到当前的主流模型中 来源:晓飞的算法工程笔记 公众号 ...
- Python爬虫初步---jupyterNptebook使用
学习视频笔记:
- 进程管理与 SELinux
进程管理与 SELinux 在 Linux 系统当中:『触发任何一个事件时,系统都会将他定义成为一个进程,并且给予这个进程一个 ID ,称为 PID,同时依据启发这个进程的用户与相关属性关系,给予 ...
- ET介绍——C#更好的协程
更好的协程 上文讲了一串回调就是协程,显然这样写代码,增加逻辑,插入逻辑非常容易出错.我们需要利用异步语法把这个异步回调的形式改成同步的形式,幸好C#已经帮我们设计好了,看代码 // example2 ...
- #线段树,矩阵乘法#洛谷 7453 [THUSCH2017] 大魔法师
题目 分析 首先考虑如果修改操作都是单点修改怎么做, 以第一种修改为例那么就是 \[\left[\begin{matrix}A\\B\\C\\1\end{matrix}\right] \times \ ...
- 前端使用 Konva 实现可视化设计器(2)
作为继续创作的动力,继续求 github Star 能超过 50 个(目前惨淡的 0 个),望多多支持. 源码 示例地址 在上一章,实现了"无限画布"."画布移动&quo ...
- 使用CMake启用RUNPATH特性
使用CMake,启用RUNPATH特性,可以参考官方帖子. 如下源码来自于上述帖子. CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR) PROJECT(R ...