这是在 GitHub 上有小伙伴报的问题,在 WPF 中,不支持调用 BitmapDecoder.Create 方法,传入的 FileStream 是配置了 FileOptions.Asynchronous 选项的文件流。本质原因是 WIC 层不支持,和 WPF 没有关系

GitHub 链接: BitmapDecoder.Create does not handle FileStream with FileOptions.Asynchronous · Issue #4355 · dotnet/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_ProxyIWICImagingFactory::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

本文所有代码放在 githubgitee 欢迎小伙伴访问

当前的 WPF 在 https://github.com/dotnet/wpf 完全开源,使用友好的 MIT 协议,意味着允许任何人任何组织和企业任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。在仓库里面包含了完全的构建逻辑,只需要本地的网络足够好(因为需要下载一堆构建工具),即可进行本地构建

WPF 已知问题 BitmapDecoder.Create 不支持传入 Asynchronous 的文件流的更多相关文章

  1. go-carbon 1.5.2版本发布, 修复已知 bug 和新增功能及葡萄牙语翻译文件

    carbon 是一个轻量级.语义化.对开发者友好的golang时间处理库,支持链式调用. 目前已被 [awesome-go](https://github.com/avelino/awesome-go ...

  2. Confluence 6 指定日志选项和已知问题

    指定 Confluence 日志选项 这里是一些特定的日志配置,你可能在对问题进行调试的时候需要. 在日志中记录数据库使用的 SQL 查询请求 你可能希望增加日志的中的内容,记录 Confluence ...

  3. Python实现加密的ZIP文件解压(密码已知)

    博主在上篇博文介绍了<Python实现加密的RAR文件解压(密码已知)>后,又尝试了ZIP文件的解压方法,下面开始分享. 当ZIP文件的压缩密码已知时,可以通过调用zipfile库进行解压 ...

  4. [推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼、百战不殆)

    原文:[推荐]ORACLE PL/SQL编程之五:异常错误处理(知已知彼.百战不殆) [推荐]ORACLE PL/SQL编程之五: 异常错误处理(知已知彼.百战不殆) 继上三篇:ORACLE PL/S ...

  5. python预科前三天:计算器知识、Python下载和安装、Pycharm下载安装激活设置、解释型和编译型、git、思维导图、显示隐藏文件、隐藏已知文件扩展名、创建组织、创建项目、提交作业、排BUG技巧

    1.计算机组成结构:CPU.硬盘.内存.输入输出设备.主板.电源. 2.硬件之间的协作关系:是CPU运算完后给操作系统.专业术语叫指令. 3.键盘输入a之后发生的事情:键盘-CPU-操作系统-显卡-显 ...

  6. WCF 已知类型和泛型解析程序 KnownType

    数据协定继承 已知类型和泛型解析程序 Juval Lowy 下载代码示例 自首次发布以来,Windows Communication Foundation (WCF) 开发人员便必须处理数据协定继承方 ...

  7. 已知json类型根据类型封装集合

    1编写帮助类根绝url得到json public static string Post(string url) { string strURL = url; //创建一个HTTP请求 HttpWebR ...

  8. 剑指offer——已知二叉树的先序和中序排列,重构二叉树

    这是剑指offer中关于二叉树重构的一道题.题目原型为: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2, ...

  9. WP8.1开发者预览版本号已知 Bug

    偶的 Lumia 920 已经升级到最新的 8.1 开发者预览版本号,使用中没有发现什么问题. 可能是由于偶玩手机的情况比較少吧!忽然看到 MS 停止此版本号的更新,并说明有非常多的 BUG,偶就郁闷 ...

  10. EF对于已有数据库的Code First支持

    EF对于已有数据库的Code First支持 原文链接 本文将逐步介绍怎样用Code First的方式基于已有数据库进行开发.Code First支持你使用C#或者VB.Net定义类.并使用数据模型标 ...

随机推荐

  1. 浅析三维模型OBJ格式轻量化压缩集群处理方法

    浅析三维模型OBJ格式轻量化压缩集群处理方法 三维模型的OBJ格式轻量化压缩是指通过一系列技术和方法将三维模型的文件大小进一步减小,以提高模型在计算机中的加载.传输和存储效率.集群处理技术是指利用多台 ...

  2. Sealos 云开发:Laf 出嫁了,与 Sealos 正式结合!

    千呼万唤始出来,Laf 云开发最近已正式与 Sealos 融合,入住 Sealos!大家可以登录 Sealos 公有云 体验和使用,现在正式介绍一下 Sealos 云开发. Sealos 云开发是什么 ...

  3. 记录--关于无感刷新Token,我是这样子做的

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 什么是JWT JWT是全称是JSON WEB TOKEN,是一个开放标准,用于将各方数据信息作为JSON格式进行对象传递,可以对数据进行可 ...

  4. 【虚幻引擎】DTWebSocketServer 蓝图创建WebSocket服务器插件使用说明

    本插件可以使用蓝图创建WebSocket服务器,并监听响应数据. 下载地址在文章最后. 1. 节点说明 Create Web Socket Server – 创建WebSocket服务器对象并开启监听 ...

  5. Avalonia项目生成银河麒麟操作系统安装包

    1 在项目根目录添加xxx.desktop文件,文件内容: [Desktop Entry] Name=xxx Type=Application Exec=/usr/share/xxx/xxx Icon ...

  6. python 1992和2006年国家标准学科分类和代码标准化并存入MySQL数据库

    数据表 代码 1 import pandas as pd 2 import pymysql 3 4 5 def get_subject_1992(): 6 res={} 7 the_former_co ...

  7. 汇编语言-使用BIOS进行键盘输入和磁盘读写

    int9中断例程对键盘输入的处理   键盘输入将引发9号中断,BIOS提供了int9中断例程.CPU在9号中断发生后,执行int 9中断例程,从60h端口读出扫描码,并将其转化为相应的ASCII码或状 ...

  8. #ST表#CF1879F Last Man Standing

    洛谷题面 CF1879F 分析 当 \(x\) 大于最大值时一定可以被约化为等于的情况,考虑枚举 \(x\), 通过枚举倍数的方式可以知道存在若干段区间消耗同一精神状态的次数是相同的,那么区别就是其精 ...

  9. #dp,齐肯多夫定理#CF126D Fibonacci Sums

    题目 \(T(T\leq 10^5)\) 组数据,每次给定数字 \(n(n\leq 10^{18})\), 问有多少种方案将 \(n\) 分解成若干个互不相同的斐波那契数 分析 如果找到一个方案使得所 ...

  10. #计数#CF10C Digital Root

    题目 定义\(d(x)\)为\(x\)的数位和嵌套,直至\(0\leq d(x)<10\) 询问在\([1\sim n]\)中有多少个三元组\((a,b,c)\)满足 \[ab\neq c,d( ...