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定义类.并使用数据模型标 ...
随机推荐
- 【Nginx】如何使用自签CA配置HTTPS加密反向代理访问?看了这篇我会了!!
写在前面 随着互联网的发展,很多公司和个人越来越重视网络的安全性,越来越多的公司采用HTTPS协议来代替了HTTP协议.为何说HTTPS协议比HTTP协议安全呢?小伙伴们自行百度吧!我就不说了.今天, ...
- KingbaseES V8R6 中walminer的使用
前言 walminer工具可以帮助dba挖掘wal日志中的内容,看到某时间对应数据库中的具体操作.例如挖掘日志后可以看到数据库某时间有哪些dml语句. walminer的限制与约束 WalMiner工 ...
- 如何在Java中读取超过内存大小的文件
读取文件内容,然后进行处理,在Java中我们通常利用 Files 类中的方法,将可以文件内容加载到内存,并流顺利地进行处理.但是,在一些场景下,我们需要处理的文件可能比我们机器所拥有的内存要大.此时, ...
- #Kruskal重构树,Dijkstra,倍增#洛谷 4768 [NOI2018]归程
题目传送门 分析 首先Dijkstra是必需的(关于SPFA,它死了233) 无向图,所以先求出1号节点到所有点的距离,然后肯定希望起点能驾驶到离一号点最短的汽车可到的地方 但是怎么办,考虑海拔大的边 ...
- #博弈论#HDU 2516 取石子游戏
题目 \(n\)个石子,两人轮流取.先取者第1次可以取任意多个, 但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍. 取完者胜.先取者负输出"Second win".先取 ...
- #Dijkstra,二进制拆位#洛谷 5304 [GXOI/GZOI2019]旅行者
题目 分析(\(logk\)次Dijkstra) 首先为什么\(O(nklogn)\)的多次\(dijkstra\)为什么会TLE, 因为中间有许多的冗余状态,即使两点求出的路径是最短的,它也不一定是 ...
- #线段树、构造#A 或位运算
题目 一个长度为\(n\)的非负整数序列, 需要满足\(m\)个区间或值为阈值的限制条件 现在要构造一个这样的序列,不存在输出No 分析 线段树支持区间与,但查询区间或,下传标记,那就很好做了 代码 ...
- #dp,模型转换,排列组合#AT1983 [AGC001E] BBQ Hard
题目 有两个长度为\(n\)的序列\(a,b\),需要求 \[\sum_{i=1}^n\sum_{j=i+1}^nC(a_i+b_i+a_j+b_j,a_i+a_j) \] 其中\(n\leq 200 ...
- Jetty的https模块
启用https模块,执行如下命令: java -jar $JETTY_HOME/start.jar --add-modules=https 命令的输出,如下: INFO : https initial ...
- OpenHarmony装饰指定自定义组件:@BuilderParam装饰器
当开发者创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操作.若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能.为解决此问题,ArkUI ...