While having some fun with the Visual Studio SDK I ran across a great example of a Navigation window by Gaston Milano. It's a great sample to start with if you would like to dive into Visual Studio Package development. One item of note that I found while using the navigation window on a very large project is that Visual Studio 2005 kept crashing everytime the window would be brought up. I noticed that before VS crashed it had a difficult time drawing things. I immediately thought of a GDI leak. I opened up Task Manager and checked the GDI objects and sure enough every time I openned the window the GDI object count would skyrocket upwards, never to return to a sane GDI count again. VS crashed after reaching a count of about 10,000 GDI objects and I don't blame it for doing that either :)
The offending routine, the getter for the HierarchyItem::Icon property in HierarchyItem.cs, calls a method NativeMethods.ImageList_GetIcon() which merely vectors off into the win32 version of ImageList_GetIcon(). Looking at the MSDN documentation for this routine you'll notice that the remarks at the end state
"It is the responsibility of the calling application to destroy the icon returned from this function using the DestroyIcon function"
Looking at the code you'll notice that DestroyIcon is never called on the temporary HICON returned from this call so every time this routine is called I noted 2 HICONs being leaked. And on my large project, this really started adding up.
The fix?
Change the following code in HierarchyItem.cs:
int handleIcon = NativeMethods.ImageList_GetIcon((int)imageList, (int)index, hbmMask);
try {
this.icon = (Icon)Icon.FromHandle((IntPtr)handleIcon).Clone();
}
catch (ArgumentException) {
}
To:
int handleIcon = NativeMethods.ImageList_GetIcon((int)imageList, (int)index, hbmMask);
try {
this.icon = (Icon)Icon.FromHandle((IntPtr)handleIcon).Clone();
NativeMethods.DestroyIcon(handleIcon); // <== added to destroy the temp HICON
} catch (ArgumentException) {
}
And add a reference to DestroyIcon in the NativeMethods class like so:
[DllImport("USER32")]
public static extern int DestroyIcon(int HICON);
Hope that helps any of you running into similar problems.