WpfHelper Class
Provides helper methods for dealing with Windows Presentation Foundation (WPF) dispatcherresource leaks in multithreaded applications.
- Inheritance:
- System.ObjectWpfHelper
Remarks
GemBox uses or might use WPF for the following operations: printing, converting to an XpsDocument or an ImageSource, saving to an XPS file or an image file, getting page or drawing content as FrameworkElement, bitmap decoding and encoding, rasterizing the vectorized content, text formatting (shaping), converting colors between custom color spaces, transferring data to and from the system Clipboard, and other. For some of these operations, such as printing and converting to an ImageSource, the use of WPF is mandatory because no alternative is currently provided or possible. For other operations, an alternative to WPF might be used instead. For example, for saving to an XPS file or an image file and rasterizing the vectorized content, Skia (via SkiaSharp NuGet package) might be used instead. For bitmap decoding and encoding, GDI+, Skia, or internal GemBox implementation might be used instead. And for text shaping, HarfBuzz (via HarfBuzzSharp NuGet package) or internal GemBox implementation might be used instead.
If WPF is used in a non-WPF application or in a WPF application but not in its main (UI) thread, the instance of a Dispatcher class is implicitly created for each thread in which WPF is used and this instance must be shutdown before the thread terminates, otherwise the resource leak happens. The Dispatcher shutdown must be invoked in the same thread in which the Dispatcher was created.
important
Since GemBox doesn't know if the code running after the GemBox code in the same thread might use WPF (and therefore require a Dispatcher instance that is not shutdown) nor GemBox has an option to be notified before a thread terminates (to shutdown the Dispatcher then and prevent the resource leak), GemBox won't use WPF in a thread that doesn't already have an associated Dispatcher or if it was already shutdown. In other words, GemBox guarantees that its code won't initiate a creation of a Dispatcher instance in any currently existing thread, except those listed below, thus preventing the resource leak. Therefore, for operations for which the use of WPF is not mandatory, an alternative to WPF will be used instead, and for operations for which the use of WPF is mandatory, GemBox will create a new thread, associate a new Dispatcher with it, use WPF in that thread, and shutdown the Dispatcher before that thread terminates thus preventing the resource leak. In the meantime, the current thread will wait for this thread to terminate via the Join method.
For reasons explained below, GemBox might still initiate a creation of a Dispatcher instance in the following threads without shutting it down, thus allowing the resource leak to happen:
- The thread in which you set the DispatcherForCurrentThreadMustBeShutdown to false because with this operation you explicitly allow the thread to leak resources.
- The main (primary) thread of your application because when it terminates, the application's process exists and the operating system reclaims all used resources.
- Threads not in the defaultapplication domain when running on .NET Framework because that application domain should be unloaded at some point reclaiming all resources used by it, hopefully.
This legacy behavior is maintained because some of our users have their code running in sandboxed environment. - Threads whose name ends with 'GemBox WPF background thread' because GemBox component and testing infrastructure relies on this.
If you want GemBox to use WPF (for operations for which the use of WPF is mandatory) or to consider using WPF (for operations for which an alternative to WPF might be used instead) in the current thread, you must create a Dispatcher instance for the current thread and you mustshutdown the Dispatcher before that thread terminates to prevent the resource leak. A utility method CreateDispatcherForCurrentThread() can be used for this purpose. The returned WpfHelper.DispatcherShutdownermust be disposed before the current thread terminates to shutdown the IsDispatcherNewlyCreatedDispatcher and prevent the resource leak. If the IsDispatcherShutdown of the returned WpfHelper.DispatcherShutdowner is false then GemBox will use or consider using WPF in the current thread; otherwise, if the value is true, GemBox won't use WPF in the current thread because its Dispatcher is already shutdown.
important
You should create and shutdown the Dispatcher instance for the current thread (for example, with the CreateDispatcherForCurrentThread() method and the returned WpfHelper.DispatcherShutdowner) only if you created the current thread or it is the main (primary) thread of your application. Otherwise, the thread might belong to the managed thread pool and/or the code running after your code in that thread might use WPF and therefore require a Dispatcher instance that is not shutdown.
Properties
DispatcherForCurrentThreadMustBeShutdown
Gets or sets the value whether the Dispatcher instance associated with the current thread must be shutdown before the current thread terminates in the context of verification done by the VerifyMaxNonShutdownDispatcherCount() method.
The default list of threads whose associated Dispatcher instance doesn't have to be shutdown are listed in the remarks of the WpfHelper class.
public static bool DispatcherForCurrentThreadMustBeShutdown { get; set; }
Public Shared Property DispatcherForCurrentThreadMustBeShutdown As Boolean
Property Value
- System.Boolean
true to include the current thread to the the verification done by the VerifyMaxNonShutdownDispatcherCount() method; otherwise, false to exclude the current thread from the the verification done by the VerifyMaxNonShutdownDispatcherCount() method thus allowing it to leak resources.
Remarks
For diagnostics purposes of reliably detecting the dispatcherresource leaks in multithreaded applications, you can set the value of the MaxNonShutdownDispatcherCount property to a non-negative value and optionally use VerifyMaxNonShutdownDispatcherCount() in each newly created thread. If you want the current thread to be excluded from this verification and allow it to leak resources, set the DispatcherForCurrentThreadMustBeShutdown to false. Setting the DispatcherForCurrentThreadMustBeShutdown to false also enables GemBox to use or consider using WPF in the current thread without GemBox shutting down the associated Dispatcher instance thus allowing GemBox code to leak resources.
MaxNonShutdownDispatcherCount
Gets or sets the maximum number of Dispatcher instances associated with terminated threads that have not been shutdown while their associated thread was alive, thus leaking resources.
If the value is negative, then counting of Dispatcher instances associated with terminated threads that have not been shutdown while their associated thread was alive is not performed.
Default value: -1.
public static int MaxNonShutdownDispatcherCount { get; set; }
Public Shared Property MaxNonShutdownDispatcherCount As Integer
Property Value
- System.Int32
The maximum number of Dispatcher instances associated with terminated threads that have not been shutdown while their associated thread was alive.
Remarks
For diagnostics purposes of reliably detecting the dispatcherresource leaks in multithreaded applications, you can set the value of the MaxNonShutdownDispatcherCount property to a non-negative value and optionally use VerifyMaxNonShutdownDispatcherCount() in each newly created thread. If you want the current thread to be excluded from this verification and allow it to leak resources, set the DispatcherForCurrentThreadMustBeShutdown to false. Setting the DispatcherForCurrentThreadMustBeShutdown to false also enables GemBox to use or consider using WPF in the current thread without GemBox shutting down the associated Dispatcher instance thus allowing GemBox code to leak resources.
Methods
CreateDispatcherForCurrentThread()
Creates a Dispatcher instance for the current thread if it was not already created.
The returned WpfHelper.DispatcherShutdownermust be disposed before the current thread terminates to shutdown the IsDispatcherNewlyCreatedDispatcher and prevent the resource leak.
Use this method if you want GemBox to use WPF (for operations for which the use of WPF is mandatory) or to consider using WPF (for operations for which an alternative to WPF might be used instead) in the current thread. If the IsDispatcherShutdown of the returned WpfHelper.DispatcherShutdowner is false then GemBox will use or consider using WPF in the current thread; otherwise, if the value is true, GemBox won't use WPF in the current thread because its Dispatcher is already shutdown.
important
You should call this method only if you created the current thread or it is the main (primary) thread of your application. Otherwise, the thread might belong to the managed thread pool and/or the code running after your code in that thread might use WPF and therefore require a Dispatcher instance that is not shutdown.
For more information, read the WpfHelper class remarks.
public static WpfHelper.DispatcherShutdowner CreateDispatcherForCurrentThread()
Public Shared Function CreateDispatcherForCurrentThread As WpfHelper.DispatcherShutdowner
Returns
A WpfHelper.DispatcherShutdowner instance that must be disposed before the current thread terminates to shutdown the IsDispatcherNewlyCreatedDispatcher and prevent the resource leak.
VerifyMaxNonShutdownDispatcherCount()
Iterates over the list of threads in which VerifyMaxNonShutdownDispatcherCount() was called and counts the number of Dispatcher instances associated with terminated threads that have not been shutdown while their associated thread was alive, thus leaking resources, and throws an System.OutOfMemoryException if that number is greater than the MaxNonShutdownDispatcherCount.
If the value of the MaxNonShutdownDispatcherCount is negative, then this method does nothing.
public static void VerifyMaxNonShutdownDispatcherCount()
Public Shared Sub VerifyMaxNonShutdownDispatcherCount
Remarks
For diagnostics purposes of reliably detecting the dispatcherresource leaks in multithreaded applications, you can set the value of the MaxNonShutdownDispatcherCount property to a non-negative value and optionally use VerifyMaxNonShutdownDispatcherCount() in each newly created thread. If you want the current thread to be excluded from this verification and allow it to leak resources, set the DispatcherForCurrentThreadMustBeShutdown to false. Setting the DispatcherForCurrentThreadMustBeShutdown to false also enables GemBox to use or consider using WPF in the current thread without GemBox shutting down the associated Dispatcher instance thus allowing GemBox code to leak resources.
Exceptions
- System.OutOfMemoryException
The application is leaking more resources that specified by the MaxNonShutdownDispatcherCount limit. Please read the documentation of the WpfHelper class for more information how to prevent the resource leak.