GOTCHA 65 Release of COM object is confusing


GOTCHA #65 Release of COM object is confusing

Programming with COM in C++ has always been quite a task. You can recognize C++ COM programmers by the scars on their bodies. By comparison, VB6 COM programming is easier. I have heard that one of Microsoft's goals for .NET is to make COM interoperability as simple as possible. Unfortunately, they have reached something of a middle ground, and even VB.NET programmers have to do some extra work now to clean up COM objects.

In C++, you have to call Release() on an interface pointer when you are done with the COM object. Forgetting to call Release() leaves the COM component stuck in memory. In VB6, setting the reference to Nothing is sufficient; the call to Release() is done automatically.

In .NET, you use a Runtime Callable Wrapper (RCW) to communicate with the COM component. As you would guess, the RCW takes care of releasing the component when it is cleaned up. However, you don't have much control over the timing of the garbage collection. So, the logical thought is to Dispose() the RCW when you're done with it. Sorry, the RCW doesn't implement the IDisposable interface. It would be easier if it did, wouldn't it? So what should you do?

Suppose you have a COM component (not shown here) which pops up a message box (just for demo purposes) when an object is created and also when it's destroyed. Look at the .NET code that communicates with this component in Example 8-1.

Example 8-1. Problem releasing the COM object

C# (ThouShaltReleaseObject)

 using System; namespace COMCompUser {     class Test     {         [STAThread]         static void Main(string[] args)         {             Console.WriteLine("Creating object");             MyCOMCompLib.IMyComp theMyComp                 = new MyCOMCompLib.MyCompClass();             Console.WriteLine("Calling Method1");             theMyComp.Method1();             //Console.WriteLine("Releasing the object");             //System.Runtime.InteropServices.Marshal.ReleaseComObject(             //    theMyComp);             Console.WriteLine("Setting reference to null");             theMyComp = null;             Console.WriteLine("Has the Object been destroyed?");             Console.ReadLine();         }     } } 

VB.NET (ThouShaltReleaseObject)

 Module Test     Sub Main()         Console.WriteLine("Creating object")         Dim theMyComp As MyCOMCompLib.IMyComp _             = New MyCOMCompLib.MyCompClass         Console.WriteLine("Calling Method1")         theMyComp.Method1()         'Console.WriteLine("Releasing the object")         'System.Runtime.InteropServices.Marshal.ReleaseComObject( _         '    theMyComp)         Console.WriteLine("Setting reference to null")         theMyComp = Nothing         Console.WriteLine("Has the Object been destroyed?")         Console.ReadLine()     End Sub End Module 

In this example you create an RCW for the COM component. You call Method1() on it, then set the reference to the RCW to null/Nothing. As the above code executes, you get the output shown in Figures 8-1 through 8-3.

Figure 8-1. Object created


The object is created (Figure 8-1) and Method1() is called (Figure 8-2). However, the object has not been destroyed yet (Figure 8-3). Pressing Return now, in response to Console.ReadLine(), will pop up the message box showing that the object has been destroyed. As you can see, setting the reference to null/Nothing does not release the object, a departure from COM component interaction in VB6.

Figure 8-2. Calling Method1()


Figure 8-3. Has the object been destroyed?


How can you clean up the object? As I mentioned, there is no Dispose() method you can call using the RCW reference. To properly release the object in Example 8-1, uncomment the two commented-out statements in the Main() method. This gives you:

     Console.WriteLine("Releasing the object");     System.Runtime.InteropServices.Marshal.ReleaseComObject(         theMyComp); 

When you are done using the component, you invoke the System.Runtime.InteropServices.Marshal.ReleaseComObject() static/Shared method. This releases the COM component at that very moment without waiting for the garbage collector. (It's better to call ReleaseComObject() from a finally block to make sure the object is released even if an exception occurs.)

Should you call ReleaseComObject() as soon as you're done using a COM component? Well, if you don't call it, the object is held until the garbage collector eventually (and much later than you might desire), decides to clean it up. The program could end up holding critical resources for an extended period of time, and this might also affect the overall performance of the system.

However, calling ReleaseComObject() has risks. (The next gotcha discusses one of them.) If you are not worried about resources being held for too long, then do not bother using ReleaseComObject(). This is especially true for client applications. If, on the other hand, you are concerned about out-of-process unmanaged resources being held (especially in a server application), then you need to use ReleaseComObject().

For insight into other problems with ReleaseComObject(), see the blogs referenced in "ReleaseComObject() issues" in the "On the Web" section of the Appendix.

IN A NUTSHELL

When you are done using a COM component, if you want any critical resources to be cleaned up right away, release it using the ReleaseComObject() static/Shared method of the System.Runtime.InteropServices.Marshal class. Do so in a finally block. Note the risks with ReleaseComObject(), however.

SEE ALSO

Gotcha #66, "Using interface pointers after calling ReleaseComObject() will fail" and Gotcha #70, "Spattering access to COM components makes code hard to maintain."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net