Microsoft KB Archive/319799

= BUG: NullReferenceException Is Thrown by an Unmanaged Object After IVsaEngine.Reset =

Article ID: 319799

Article Last Modified on 5/16/2002

-

APPLIES TO


 * Microsoft Visual Studio for Applications SDK 1.0

-



This article was previously published under Q319799



SYMPTOMS
A managed Visual Studio for Applications (VSA) host creates an instance of an unmanaged object by using a Runtime Callable Wrapper (RCW). The host adds the unmanaged object as an AppGlobal object, by using IVsaEngine.Items.CreateItem and IVsaSite.GetGlobalInstance. When the host calls IVsaEngine.Reset, the RCW loses its connection to the unmanaged object. This causes a System.NullReferenceException error if the host tries to make a call on the RCW.



CAUSE
When an unmanaged VSA host adds an unmanaged AppGlobal object to the VSA engine, a RCW is automatically created for the object. A COM reference to the unmanaged object is maintained by the RCW until it is cleaned up by the Microsoft .NET garbage collector. Because .NET uses a &quot;lazy&quot; garbage-collection scheme, this reference may take some time to be released. This may prevent the unmanaged host application from quitting cleanly.

To resolve this issue, the IVsaEngine.Reset method calls Marshal.ReleaseComObject on any unmanaged AppGlobal objects that have been added to the engine. This decrements the reference count on the COM object and permits the host to quit cleanly. Unfortunately, this resolution does not work in the case of a managed VSA host that uses unmanaged AppGlobal objects. Because the managed host maintains the RCW for the unmanaged AppGlobal object, it is incorrect for VSA to call Marshal.ReleaseComObject.



RESOLUTION
There are two possible resolutions.

The first resolution is to write a managed wrapper class for the AppGlobal object, and then to pass that wrapper class to VSA. Because the custom wrapper class is not an RCW, VSA does not try to release it. This is the better solution, because you can also add .NET attributes (such as security attributes) if you must.

If you cannot write a custom wrapper class for your object, the second resolution is to take advantage of the manner in which RCW referencing works to keep an artificial reference count on the object that you give to VSA. When a COM interface is passed to managed code, the .NET runtime looks through the existing RCWs to determine if there is already an RCW for that object. If it finds that a RCW already exists, it adds a reference to the RCW instead of creating a new RCW. When Marshal.ReleaseComObject is called during the reset, the RCW is decremented and is not &quot;zombied&quot; for garbage collection.

To do this, add a method to your COM object that returns an interface pointer for that same object again. Pass this reference to the VSA engine in IVsaSite.GetGlobalInstance. When you call IVsaEngine.Reset, this additional reference is decremented, but the RCW keeps its connection to the unmanaged object.



STATUS
Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.



Runtime Callable Wrapper Reference Counting
To take advantage of RCW reference counting, follow these steps:   Add a property to the unmanaged COM object that returns an interface pointer to the COM object. In Microsoft C++, this might be similar to the following sample code: /************************************************************************ STDMETHODIMP TestObject::get_SelfReference( IDispatch** pmySelf ) {  TRACE(&quot;TestObject::get_SelfReference\n&quot;); return this->QueryInterface( IID_IDispatch, (void**)&pmySelf ); }                     Modify the implementation of your VSA host to use the new SelfReference property. In Microsoft C#, your IVsaSite.GetGlobalInstance method might be similar to the following sample code: public System.Object GetGlobalInstance ( System.String name ) {   //Check the name of the global instance. if (String.Compare( name, &quot;Application&quot; ) == 0) {     //Your C# object is a managed object, so it can return a      //direct reference to VSA. return this; }  else if (String.Compare( name, &quot;TestObject&quot; ) == 0) {     //TestObject is unmanaged, so it has a Runtime Callable Wrapper. //Return a new self-reference so that the RCW is refcounted. return m_TestObject.SelfReference; }
 * get_SelfReference -- This method returns a reference to the TestObject.
 * Parameters: pmySelf -- address of an IDispatch interface pointer
 * Returns: Any value that is returned by IUnknown::QueryInteface

//If the name is not found, throw an ItemInvalidName exception. else throw new VsaException( VsaError.ItemNameInvalid, name +         &quot; undefined.&quot; ); }                   

