In our application, we are using PngBitmapEncoder to encode and save PNG image in a separate thread\task. After few days of running the application we are seeing Dispatcher cannot be created from Encoder and throws error
Not enough storage is available to process the command
And has the below call stack
System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command
at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
at System.Windows.Threading.Dispatcher..ctor()
at System.Windows.Threading.DispatcherObject..ctor()
at System.Windows.Media.Imaging.BitmapEncoder..ctor(Boolean isBuiltIn)
As .Net is available open source, got curious of what line inside Dispatcher constructor is throwing the error
[SecurityCritical, SecurityTreatAsSafe]
private Dispatcher()
{
_queue = new PriorityQueue<DispatcherOperation>();
_tlsDispatcher = this; // use TLS for ownership only
_dispatcherThread = Thread.CurrentThread;
// Add ourselves to the map of dispatchers to threads.
lock(_globalLock)
{
_dispatchers.Add(new WeakReference(this));
}
_unhandledExceptionEventArgs = new DispatcherUnhandledExceptionEventArgs(this);
_exceptionFilterEventArgs = new DispatcherUnhandledExceptionFilterEventArgs(this);
_defaultDispatcherSynchronizationContext = new DispatcherSynchronizationContext(this);
// Create the message-only window we use to receive messages
// that tell us to process the queue.
MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();
_window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window );
_hook = new HwndWrapperHook(WndProcHook);
_window.Value.AddHook(_hook);
// DDVSO:447590
// Verify that the accessibility switches are set prior to any major UI code running.
AccessibilitySwitches.VerifySwitches(this);
}
Update
Updated the constructor code from .net open source. The dispatcher.cs is available here https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs,078d6b27d9837a35
After some investigation we found that issue occurs after some 15000 iterations(each iteration creates a new thread and calls PngBitmapEncoder). Then found that this is linked to Global Atom Table limit (0x4000 or 16384). More details on Global Atom Table here https://learn.microsoft.com/en-us/archive/blogs/ntdebugging/identifying-global-atom-table-leaks
The dispatcher created each time makes an entry in the Global atom table and on thread exit this entry is not cleared. This leads to leak in Global atom table and when it reaches the max limit, it throws "Not enough storage...." error. This seems like a issue with Microsoft's handling of Dispatcher. Even the PngBitmapEncoder documentation, I do not see any remark with respect to Dispatcher handling and any explicit shutdown of dispatcher.