Memory Usage and Resource Disposal
The garbage collector of .NET frees objects that nobody refers to any more, but it doesn't decide when a ServerTextControl gives back what it holds in Windows. That is the job of your code, and in a WPF application that runs for a whole working day, an instance that is never disposed adds up. What follows shows what each part of TX Text Control keeps in memory, which ones your code has to dispose, and how to find the instances that are left behind before Windows runs out of handles.
What Each Part Keeps
| Part | Keeps | What your code does |
|---|---|---|
The WPF TextControl in a window | Its document, the undo buffer and what it renders for the screen | Nothing to dispose: the control doesn't implement IDisposable. Keep the undo buffer small, as described below |
A ServerTextControl | Its document, without undo buffer and without rendering | Dispose it, in a using block |
A MailMerge | No document of its own; it merges into the document of its TextComponent | Dispose it; it is a component |
| Byte arrays and copies of documents | Whatever your code stores in them | Drop the reference when the copy isn't needed any more |
A ServerTextControl keeps no undo history and renders no images, and Text Control names its smaller memory footprint as one of its advantages over a visible control. Documents the user doesn't see are therefore best created there, as Using ServerTextControl in Desktop Applications describes.
Dispose Every ServerTextControl
Text Control states that a ServerTextControl must be disposed explicitly and recommends a using block, which disposes the instance even when an exception leaves it; Create and Dispose Each Instance shows the pattern.
Warning
Undisposed Instances. When instances aren't disposed, Create eventually returns
false. The most typical cause is that no new window handle can be created, because Windows allows a process 10,000 USER handles and 10,000 GDI handles. The instance then exists but can't be used, and loading a document into it fails with "The TXTextControl object must have been completely loaded to use this method." Text Control describes such failures as appearing after days and sporadically, and names instances that weren't disposed as their most likely cause. A restart of the application releases the handles; disposing the instances prevents the problem.
A MailMerge that your code creates is disposed the same way, in a using block, as the samples of Mail Merge Performance show.
Watch Instances and Handles
To find instances that are never disposed, count them. The following class, based on Text Control's diagnostics sample, creates ServerTextControl instances, counts those not yet disposed and reads the USER and GDI handles of the process. Add it to your project as ServerTextControlFactory.cs:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using TXTextControl;
// Creates ServerTextControl instances and counts those that aren't disposed yet
public static class ServerTextControlFactory
{
private static int liveInstances;
[]
private static extern int GetGuiResources(IntPtr hProcess, int uiFlags);
public static int LiveInstances => liveInstances;
// 0 counts GDI objects, 1 counts USER objects
public static int GdiHandles => GetGuiResources(Process.GetCurrentProcess().Handle, 0);
public static int UserHandles => GetGuiResources(Process.GetCurrentProcess().Handle, 1);
public static ServerTextControl Create()
{
ServerTextControl tx = new ServerTextControl();
if (!tx.Create())
{
tx.Dispose();
throw new InvalidOperationException("ServerTextControl could not be created.");
}
Interlocked.Increment(ref liveInstances);
tx.Disposed += (sender, e) => Interlocked.Decrement(ref liveInstances);
return tx;
}
}
Create your instances through the factory, and show the counters, for example in a TextBlock named statusText at the bottom of MainWindow.xaml:
private void Convert_Click(object sender, RoutedEventArgs e)
{
using (ServerTextControl tx = ServerTextControlFactory.Create())
{
tx.Load("contract.docx", StreamType.WordprocessingML);
tx.Save("contract.pdf", StreamType.AdobePDF);
}
statusText.Text = $"Instances: {ServerTextControlFactory.LiveInstances}, " +
$"USER handles: {ServerTextControlFactory.UserHandles}, " +
$"GDI handles: {ServerTextControlFactory.GdiHandles}";
}
The code works as follows:
- As a component,
ServerTextControlraises itsDisposedevent when it is disposed, and the handler lowers the counter.Interlockedkeeps the count right when background threads create instances. GetGuiResourcesof User32.dll returns the number of GDI objects or USER objects of the process, the two resources Windows limits.- A counter that stays above zero after the work is done, or handle counts that keep rising, point to instances that are never disposed.
The blog article Diagnostics: When Calling ServerTextControl.Create() Failed contains the original sample, which also lists the live instances and can dispose all of them while you debug.
Keep the Undo Buffer Small
The WPF TextControl records every change so that the user can undo it, and the buffer grows with each change. Switch it off while your code generates a document, and empty it after your code has loaded one; Undo and Redo shows how.
When Memory Runs Out
- Errors while loading or saving. A FilterException whose Reason is
OutOfMemorymeans that TX Text Control couldn't allocate enough memory;FileTooBigmeans that the input file is too big. Exceptions and Error Handling shows how to catch aFilterException. - A 32-bit process. A 64-bit build has far more memory available; 32-bit and 64-bit Applications shows how to configure it.
- Documents in view models. A document kept as a byte array in a view model, a list of recent files or an auto-save copy stays in memory as long as something refers to it. Store such copies in a file or a database when they aren't needed right away.
Next Steps
- Large Documents — build and process long documents efficiently.
- Background Operations — one
ServerTextControlper thread. - Using ServerTextControl in Desktop Applications — create and dispose instances.
- Performance Best Practices — all measures at a glance.