While at Solid Edge University in Nashville, Jason demonstrated two add-ins that were running in the same app domain that could affect each other when using ReleaseComObject or (especially) FinalReleaseComObject. His demo relied on one add-in storing a system com object (.NET's "RCW") and another add-in getting the same solid edge object (RCW) and then doing a ReleaseComObject too may times (or perhaps it was a call to FinalReleaseComObject that spins the count down to zero by calling ReleaseComObject in a loop until it returns zero). The result was that every .NET client holding onto the same object in the same app domain found their RCW was disconnected from the underlying COM object.
This is indeed a big issue that Jason brought up. I think Jason pointed this out and mentioned shimming add-ins so that they run in separate app domains as one solution to the issue.
But there is another approach that avoids this issue. If you need to store a Solid Edge object (actually an RCW) in your .NET add-in (macro etc.) and you want to avoid any cross-talk with another .NET client, use the .NET Marshal object to create a unique RCW and store it instead. You can rest assured that no other .NET client can affect this RCW by over releasing one of their variables using ReleaseComObject or FinalReleaseComObject.
To protect yourself use System.Runtime.InteropServices.Marshal.GetUniqueObjectForIUnknown to get a unique RCW. Store that RCW and not the one you might get from an event or a call to an API. Here is an example of how to do this to store a Model object:
pModels = partdoc.Models
If Not pModels Is Nothing Then
Dim pThisModel = pModels.Item(1)
If Not pThisModel Is Nothing Then
' Store the model in my "pModel" instance data
pModel = System.Runtime.InteropServices.Marshal.GetUniqueObjectForIUnknown(System.Runtime.InteropServices.Marshal.GetIUnknownForObject(pThisModel))
pModels = Nothing
Posted by: R.D. Holland Post date: 8/10/2012 10:36:44 AM
Thanks for the tip. I do have an observation on your proposed solution. Per MSDN, you are supposed to release the IntPtr returned from Marshal.GetIUnknownForObject() by using Marshal.Release(). Your code does not properly release the IntPtr thus leaving an additional dangling reference. Simple enough to correct, just wanted to mention it in case anyone uses this alternative.
Posted by: Jason Newell Post date: 8/14/2012 12:38:59 PM