Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

c# help disposing of COM server object instance

Every time I get any kind of object from a COM server dll, I  want to immediately copy the data out of that object and deterministically destroy it. I may NOT force a garbage collection to free all unused objects, but must explicitly find and release all that exist. When I've finished with all of them, I call the Win32 API function CoFreeUnusedLibraries using Pinvoke to flush the COM server DLL right out of the system. I verify that the client program no longer contains the COM server DLL by using the Process Explorer utility program  from sysinternals.  My problem is that the instances never get destoyed even though I call the Dispose method at the end of my constructor.  I can call methods and access properties of the instance even after it is supposedly destroyed, making it obvious that the thing is still alive.  I think it is definilty something in my Dispose() function, but what I don't know.  Here are my Dispose functions (1 override):


public void Dispose()
{            
  Dispose(true);
 
  //Prevent subsequent finalization
  GC.SuppressFinalize(this);        
}

protected void Dispose(bool Disposing)
{
  if (!IsDisposed)
    {
      if(Disposing)
        {
            //clean up managed resources --NO CODE HERE
        }
      //clean up unmanaged resources  -- NO CODE HERE        
    }

  IsDisposed=true;  //THIS IS A BOOLEAN CLASS PROPERTY
                                //INITIALLY SET TO FALSE
}


Is there some line of code that I am missing that I'm just not finding in any of the references i've been using?  For example, something like:
    this = nothing;

All the books/tutorials i've read on Dispose are roughly like this...but I'm not seeing what line of code is supposed to destroy the instance...I'm learning c# right now, and i've been on this for a couple days now.  Any help would be appreciated.  Thanks.

grover
Tuesday, May 24, 2005

grover,

When you're finished with the COM library, are you calling InteropServices.Marshal.ReleaseComObject? This is required for explicit control of a COM object used from managed code, to decrement the RCW's reference count. You should call this before setting the COM object variable to null/Nothing.

Mark Pearce
Tuesday, May 24, 2005

Thanks, that was definitly needed, but now i've got a weird problem...I hate asking multiple questions, but i've been on this thing for days and there's not much out there for reference on COM object deallocation that is helping me.

I'm just testing creation and release of a COM object, and it seems to work succesfully by using process explorer if I comment out this statement:  numCodes = RadioCodes.GetIncidentCodes().Count();
For some reason though if i use this statement, the object does not seem to release itself and flush successfully.  In other words, just creating the instance seems to have allow me to release and flush, but actually using the instance method for some reason prevents this from happening.  I tried adding in a bunch of code after this just to see if maybe there was some delay, but the COM object seems to stick around?  Anyone know what I'm missing here?  All I'm doing is calling an instance method that returns an integer, but that seems to screw up the release and flush.

//create instance of COM object
IE237LegacyComponent.Class1Class RadioCodes = 
  new CSCIE237LegacyComponent.Class1Class();                        
//determine the number of objects to create
//NOTE:  execution of this line seems to prevent release
//and flush of object and com dll, if i comment it out it works
//fine
numCodes = RadioCodes.GetIncidentCodes().Count();
                    
Marshal.ReleaseComObject(RadioCodes);
            
GC.SuppressFinalize(RadioCodes);
            
RadioCodes = null;
            
//use pinvoke to call CoFreeUnusedLibraries, which flushes
//COM dll
Win32.CoFreeUnusedLibraries();

grover
Wednesday, May 25, 2005

I also tried the GC.Collect() method to perform a sweep, followed by a sleep(10000) method to give the app some time to finish the sweep (this program is very small so far so I would hope thats enough..I'm also not doing any threading).  Same result.  I think somehow the library is considered still being in use, but stepping through the debugger i cannot find any reference to it once i set it to null.  I'd hate to spend so much time on this thing only to give up and let the GC take care of it. 

grover
Wednesday, May 25, 2005

You need to call Marshal.ReleaseComObject on each COM object you instantiate, explicitly or implicitly.

Presumably GetIncidentCodes() is returning a reference to a COM object: you will need to free this COM object as well.

So your code needs to be something like:

IncidentCodesType incidentCodes = RadioCodes.GetIncidentCodes();
numCodes = incidentCodes.Count();
                   
Marshal.ReleaseComObject(incidentCodes);
Marshal.ReleaseComObject(RadioCodes);
           
If you are careful to ensure Marshal.ReleaseComObject is called on every COM object, there should be no need to call GC.Collect.

There is a KB article which describes a similar situation with Excel automation:

http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q317109

This all makes working with COM object hierarchies rather messy.

Joe
Wednesday, May 25, 2005

Thanks.  I'll give it a shot. 

GetIncidentCodes returns a vbacollection, and is a method of the com object instance.  I'll  post my results.  Thanks again.

grover
Wednesday, May 25, 2005

Success!!!  Thanks again. 

grover
Thursday, May 26, 2005

Grover,

This is a good post on the "under the hood" details of Marshall.ReleaseComObject: http://blogs.msdn.com/cbrumme/archive/2003/04/16/51355.aspx

John Rusk
Saturday, May 28, 2005

*  Recent Topics

*  Fog Creek Home