Memory bloat in Dynamics GP AddIn
MacroManager in GP Addin holds onto DexUIForms

MacroManager Object Keeping References to DexUIForms
When using DexUIForms
in Dynamics GP Visual Studio Add-Ins, make sure to remove the form from the MacroManager
during disposal. Otherwise, MacroManager
will keep a reference to it, keeping the form alive in memory even after it appears to be closed.
System.OutOfMemory Exception
Type: System.OutOfMemoryException
Message: Exception of type 'System.OutOfMemoryException'
was thrown.
A client machine crashed twice in succession, both times mid-afternoon, with an "OutOfMemory" exception.
Fortunately, the second time, the machine was available for a memory dump. A memory dump captures a snapshot of the application in memory at that moment. This allows us to dig into what went wrong.
Looking at the memory dump, I sorted the heap by the number and size of objects. It became clear that many of the Windows Forms the user had opened (and closed) were not being garbage collected by .NET, as would have been expected.
Here we can see three references to a control that is only on one form that has been closed three times.
As I knew IDisposable
was implemented, this behaviour was not expected.
Having reviewed the code again, improved the Dispose
methods, and fixed a few points where event handlers were not properly dereferenced, the forms still refused to clear from memory after being closed.
Something stood out: MacroManager
was still holding references to many of these forms.
Digging into Microsoft.Dexterity.Shell.dll
, the Microsoft DLL that provides macro support for GP forms. When a form inherits from DexBaseForm
(like DexUIForm
), on creation, it automatically adds itself to a list called knownMacroableItems
.
Here we have DexUIForm that inherits from DexBaseForm;
DexBaseForm adds itself to the MacroManager list as the base form is created;
Here we have that method.
But I couldn not find anywhere in the base library where forms are removed from this list, this is the clue I needed.
The solution
The solution was to explicitly remove the form from the MacroManager
list during disposal:
MacroManager.RemoveMacroableItem(this);
I added this line to the Dispose method of our base form class. All application forms inherit from this base, which itself inherits from DexUIForm. So now, whenever any form is disposed, it removes itself from the MacroManager list too.
After this change, I ran the application again and tested memory usage. This time, forms were properly cleared from the memory heap after closing.
Here we have one reference before form closed;
then after we can see the count to zero
Note
This is an easy trap to fall into—one that you might not notice unless you are actively profiling memory usage.
So if you are using DexUIForms, make sure you are cleaning up properly. Otherwise, your forms may quietly stick around in memory long after they are supposed to be gone.