Skip to content

BitmapDecoder.Create does not handle FileStream with FileOptions.Asynchronous #4355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
vonzshik opened this issue Mar 31, 2021 · 7 comments · Fixed by #4966
Closed

BitmapDecoder.Create does not handle FileStream with FileOptions.Asynchronous #4355

vonzshik opened this issue Mar 31, 2021 · 7 comments · Fixed by #4966

Comments

@vonzshik
Copy link

  • .NET Core Version: (e.g. 3.0 Preview1, or daily build number, use dotnet --info)
    5.0.201
  • Windows version: (winver)
    19042.867
  • Does the bug reproduce also in WPF for .NET Framework 4.8?: Yes/No
    Yes.

Problem description:

BitmapDecoder.Create throws ArgumentException ("Value does not fall within the expected range.") whenever the stream argument is FileStream with FileOptions.Asynchronous.

Actual behavior:

at System.Windows.Media.Imaging.BitmapDecoder.SetupDecoderFromUriOrStream(Uri uri, Stream stream, BitmapCacheOption cacheOption, Guid& clsId, Boolean& isOriginalWritable, Stream& uriStream, UnmanagedMemoryStream& unmanagedMemoryStream, SafeFileHandle& safeFilehandle)
  at System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache)
  at System.Windows.Media.Imaging.BitmapDecoder.Create(Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption)

Expected behavior:

BitmapDecoder.Create shouldn't throw an exception.

Minimal repro:

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);
@lindexi
Copy link
Member

lindexi commented May 14, 2021

The BitmapDecoder.Create will call the IWICImagingFactory_CreateDecoderFromFileHandle_Proxy which in WIC to create the decoder. But the IWICImagingFactory_CreateDecoderFromFileHandle_Proxy can not receive the Asynchronous SafeFilehandle.

IWICImagingFactory_CreateDecoderFromFileHandle_Proxy function - Win32 apps | Microsoft Docs

@vonzshik
Copy link
Author

@lindexi thank you for taking a look. I assume there is no API for an async SafeFileHandle, yes?

The fact that I was able to get around this issue by making a proxy stream, which wraps the original FileStream (and the API you've mentioned does confirm my suspicions), makes me think that BitmapDecoder.Create does check whether a provided stream is indeed a FileStream, in which case it uses a different set of API (I guess as a sort of optimization?), unlike for other cases.

If so, maybe it's a good idea to disable this behavior whenever FileStream.IsAsync is true? Otherwise, I think making the exception more verbose will surely help solving this problem for other users.

@lindexi
Copy link
Member

lindexi commented May 14, 2021

@vonzshik I agree with you.

lindexi added a commit to lindexi/lindexi_gd that referenced this issue May 15, 2021
…rt Asynchronous

[BitmapDecoder.Create does not handle FileStream with FileOptions.Asynchronous · Issue #4355 · dotnet/wpf](dotnet/wpf#4355 )
@lindexi
Copy link
Member

lindexi commented May 15, 2021

I write a demo to show the IWICImagingFactory_CreateDecoderFromFileHandle_Proxy do not support the Asynchronous file stream.

    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;
            }
        }
    }

See https://github.com/lindexi/lindexi_gd/tree/78c73fe25229f0b50992102e59c01cd535e60c31/JemlemlacuLemjakarbabo

lindexi added a commit to lindexi/lindexi_gd that referenced this issue Aug 1, 2021
[BitmapDecoder.Create does not handle FileStream with FileOptions.Asynchronous · Issue #4355 · dotnet/wpf](dotnet/wpf#4355 )
lindexi added a commit to dotnet-campus/wpf that referenced this issue Aug 1, 2021
[BitmapDecoder.Create does not handle FileStream with FileOptions.Asynchronous · Issue dotnet#4355 · dotnet/wpf](dotnet#4355 )
lindexi added a commit to lindexi/lindexi_gd that referenced this issue Aug 2, 2021
@lindexi
Copy link
Member

lindexi commented Aug 2, 2021

@vonzshik I fixed it, see #4966

I follow your opinion, when I judge this file is asynchronous, I read the file into memory.

I wrote a demo code, and it work well. See https://github.com/lindexi/lindexi_gd/tree/20b78c5547b2ecacdd244a438ef58518469a91d7/GeafakaijawkeheahemLejeehocear

You can test the demo by publish the code with self contained.

dotnet publish --self-contained -r win-x86

bgrainger pushed a commit to Faithlife/wpf that referenced this issue May 30, 2022
[BitmapDecoder.Create does not handle FileStream with FileOptions.Asynchronous · Issue dotnet#4355 · dotnet/wpf](dotnet#4355 )

(cherry picked from commit 94ba24a)
@dipeshmsft
Copy link
Member

We have taken this PR for the current Community Test Pass. Thanks for the contribution.

dipeshmsft pushed a commit that referenced this issue Aug 12, 2022
* Fix create BitmapDecoder with async file stream.

[BitmapDecoder.Create does not handle FileStream with FileOptions.Asynchronous · Issue #4355 · dotnet/wpf](#4355 )

* Add comment
@ghost ghost removed this from the Future milestone Aug 12, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 11, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants