-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Known issue: WPF will throw COM Exception when create RenderTargetBitmap too fast #3067
Comments
My understanding of this issue is that one or more GDI handles are allocated by each 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
|
@elyoh Thank you. The RenderTargetBitmap will allocate come GDI handle and clean up quickly. The RenderTargetBitmap will allocate come GDI handle in Render method and clean it in Render method. wpf/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Imaging/RenderTargetBitmap.cs Lines 209 to 213 in ac9d1b7
I use TaskManager and found that there are less than a thousand GDI objects in this process. |
I modified your sample code by counting the GDI handles at the point when the exception is thrown: [System.Runtime.InteropServices.DllImport("User32")]
private extern static int GetGuiResources(IntPtr hProcess, int uiFlags);
private static int GetGDIHandleCount()
{
using (var process = System.Diagnostics.Process.GetCurrentProcess())
{
var gdiHandles = GetGuiResources(process.Handle, 0);
return Convert.ToInt32(gdiHandles);
}
} // RenderTargetBitmap throws COM exception when created too fast: MILERR_WIN32ERROR (Exception from HRESULT: 0x88980003)
// The count is always equal to the per-process GDI handle limit in the following registry key:
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota
Console.WriteLine("GDI handles {0}: ", GetGDIHandleCount());
Console.WriteLine(e); It always yields the following output when the exception is thrown:
In other words, The call to |
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. |
Any news? help... |
1 similar comment
Any news? help... |
@lamposu |
Bump. I'm running into this issue when I use lots of |
@matthew-a-thomas The workaround is to invoke GC and wait for pending finalizers |
@miloush Thank you. Unfortunately in my case that won't do because it introduces noticeable lag and because it increases the size of the gen 2 heap. Edit: I changed my mind. I just garbage collect when GDI handle is over a certain large number—you can P/Invoke this function to get GDI handle count—and then only every few seconds. |
I think it is necessary to provide manual control, as @miloush says.
|
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: // WPF Hack: Imaging - Force the RenderTargetBitmap to release memory handles immediately
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var fieldWicSourceHandle = typeof(RenderTargetBitmap).GetField("_wicSource", flags);
var fieldRenderTargetBitmap = typeof(RenderTargetBitmap).GetField("_renderTargetBitmap", flags);
var methodReleaseHandle = fieldRenderTargetBitmap.FieldType.GetMethod("ReleaseHandle", flags);
methodReleaseHandle.Invoke(fieldWicSourceHandle.GetValue(rtb), null);
methodReleaseHandle.Invoke(fieldRenderTargetBitmap.GetValue(rtb), null);
fieldWicSourceHandle.SetValue(rtb, null);
fieldRenderTargetBitmap.SetValue(rtb, null);
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect(); As @miloush and @lindexi note, BitmapSource should use IDisposable to release these resources and save developers this headache. |
Please fix for NET6+ and NET48. |
@JensNordenbro We are trying to find a solution, thank you. |
The workaround without forcing the GC is to manually release the handle in the RenderTargetBitmap using reflection:
|
in WPF 8.0. I still get this error when creating more than 5000 instances. Please inform me if you have new things about it
|
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:
Expected behavior:
No exception
Minimal repro:
https://github.com/dotnet-campus/wpf-issues/tree/master/RenderTargetBitmapThrowsCOMExceptionWhenCreatedTooFast
The text was updated successfully, but these errors were encountered: