wpf: Known issue: WPF will throw COM Exception when create RenderTargetBitmap too fast
- .NET Core Version: 3.1.300
- Windows version: 10.0.18362
- Does the bug reproduce also in WPF for .NET Framework 4.8?: Yes
Problem description:
When we use RenderTargetBitmap to take the control screenshots, if our do it too frequent and too fast, then we will find that the framework throws an exception.
Actual behavior:
System.Runtime.InteropServices.COMException: MILERR_WIN32ERROR
System.Runtime.InteropServices.COMException (0x88980003): MILERR_WIN32ERROR (Exception from HRESULT:0x88980003)
in System.Windows.Media.Imaging.RenderTargetBitmap.FinalizeCreation()
in System.Windows.Media.Imaging.RenderTargetBitmap..ctor(Int32 pixelWidth, Int32 pixelHeight, Double dpiX, Double dpiY, PixelFormat pixelFormat)
Expected behavior:
No exception
Minimal repro:
About this issue
- Original URL
- State: open
- Created 4 years ago
- Comments: 17 (6 by maintainers)
The workaround without forcing the GC is to manually release the handle in the RenderTargetBitmap using reflection:
Yeah, from my experience if you are running a x64 process (not the default setting anymore) and have plenty of RAM, the finalizers do eventually start kicking in.
As for fix, the BitmapSource should be IDisposable and release the handle/IWICBitmap on disposal rather than in a finalizer.
@JensNordenbro We are trying to find a solution, thank you.
The only way I have found to reliably force the cleanup of the GDI handles and ensure memory use drops immediately is with the following hack:
As @miloush and @lindexi note, BitmapSource should use IDisposable to release these resources and save developers this headache.
The GDI count increments by two every time this function here is invoked:
This is being invoked in a background dispatcher thread (a different thread than the main UI, but the thread is running a
Dispatcher
).Here’s my code:
Bump. I’m running into this issue when I use lots of
RenderTargetBitmap
instances in a parallel rendering scenario. GDI object count is hitting the upper limit of 10,000.Any news? help…
I modified your sample code by counting the GDI handles at the point when the exception is thrown:
It always yields the following output when the exception is thrown:
In other words,
FinalizeCreation()
only throws this exception when the GDI handle count reaches the per-process limit. The cause is that release of these GDI handles is not immediate, so there is no guarantee that there will be enough GDI handles left when a new RenderTargetBitmap object is created. Subsequent use of RenderTargetBitmap only succeeds once the GDI handle count has dropped below the per-process limit again.The call to
GC.WaitForPendingFinalizers();
in the original sample just reduces the probability of reaching the per-process limit before handles have been released. Increasing the delay in the task creation also helps. It is certainly hardware dependent. In fact, I had to remove the call toGC.WaitForPendingFinalizers();
and decrease the delay to get the exception to throw without running the sample for quite some time.My understanding of this issue is that one or more GDI handles are allocated by each
RenderTargetBitmap
and there is no reliable way to ensure they are cleaned up quickly once aRenderTargetBitmap
goes out of scope.According to the documentation there is a theoretical limit of 65,536 GDI handles per session and this is set to 10,000 per process in the registry.
I have hit the issue when doing lots of image tiling operations in a short time. I was able to work around it by forcing the garbage collector to clean up before there was a chance of hitting this limit. It is far from ideal as the call to
GC.Collect()
can incur a performance hit.