Sometimes spotting the cause of a leak with COM objects under .NET is very difficult. Its important to keep thinking in terms of the need to release the underlying COM objects which will be referenced so that proper clean-up can be done. When .NET wrapped COM code returns an object, you should already know to do proper clean-up on that object - such a calling ReleaseComObject on it. However, if your using a compound call (i.e. more than just calling a method of an object) you should be aware that you may still be leaking if you only released the final object.
I ran into an issue recently where the following code was causing the leak:
Dim oUserProperty as UserProperty
oUserProperty = oAppointmentItem.UserProperties.Find("MyFunProperty", True)
The leak was found while checking for leaks by running Outlook from the command line “/reportleakedrenitems” switch, executing the code in an add-in and running a second instance of Outlook from the command line again with the “/reportleakedrenitems” switch, which will cause Outlook to launch Notepad with a list of items it detected item leaks on.
Let's break this down... oAppointmentItem is one object and it has a UserProperties collection object and that object's Find method gets called. Why is this important to know?
First look at the solution needed to resolve the issue:
Dim oUserProperty As Outlook.UserProperty
Dim oUserProps As Outlook.UserProperties
oUserProps = oAppointmentItem.UserProperties
oUserProperty = oUserProps.Find("MyFunProperty", True)
If (oUserProperty IsNot Nothing) Then
oUserProperty = Nothing
If (oUserProps IsNot Nothing) Then
oUserProps = Nothing
Notice that the compound call was broken out and releases were done not just for the final returned object but also the UserProperties collection. This is needed since there are multiple operations which return objects that need to be released.
The line oAppointmentItem.UserProperties.Find("MyFunProperty", True) looks like a single call. However, its performing several different operations and with OOM this is turns into more than one operation at the COM level.
- Get the UserProperties collection and return it.
- Search through the returned collection to find the designated property and return it.
So, the old code was only releasing the final object returned in operation #2. However, the object from #1 also needed to be released. So, the original compound statement needs to be broken up so that individual operations are being done and each returned object is properly released.
One thing to remember is that all COM objects under .NET are still COM objects. With COM programming such operations would be different calls. So, code such needs to obtain a handle to the collection being used for the Find in order to release it – which means breaking out the call into separate pieces so that each item can be released by ReleaseComObject calls. Note that the equivalent code in VBA would not have an issue since VBA abstracts those calls and handles the releasing.