SDK - Preventing Memory Leaks - Using and Releasing TRIMSDK COM Objects

  • KM507190
  • 20-Sep-2008
  • 20-Sep-2008

Archived Content: This information is no longer maintained and is provided "as is" for your convenience.

Reference

Preventing Memory Leaks - Using and Releasing TRIMSDK COM Objects

Return to SDK Articles page

The ReleaseCOM(obj) Function

The function in the following sample releases the COM object passed as an arguement, and is referenced throughout this article:

public void ReleaseCOMObject(object obj)
{
    try
    {
        if (obj != null)
        {
            while (System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) > 0); //keep repeating until reference count is zero.
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

try-catch-finally

Always open Trim database connections and use Trim SDK API COM objects within try clause.

Always close database connections and release whatever TRIM COM objects in the finally clause, because it is guaranteed to be executed before control exits the try-catch-finally block.

Database db = null;
Record rec = null;

try
{
    db = new Database();
    db.Connect();

    //Do your work here 
}
catch (Exception ex)
{
    //handle exceptions here
}
finally //always be executed before we exit the try-catch block
{
    //Close / ReleaseCOM here

    if (db !=null) 
    {	
        db.Disconnect();

        ReleaseCOM(db); 
        db = null; //why not set db to null within the ReleaseCOM method itself? see below
    }

    //also release any Trim objects used within the try block and set their references to null too!
}

Objects are set to null in the finally clause. Don’t set to null inside the ReleaseCOM function as shown in the following sample code.

Object anObject = new Object();

SetObjectToNull(anObject);

...

public void SetObjectToNull(Object o)
{
    o = null; //set o to null
}

* Object anObject will still NOT be null since it is passed by value, not by reference. The reference variable o is not the same as the reference variable anObject. Thus setting o to null, has no effect on anObject.

* On the other hand if o was passed by reference (using the ‘ref’ keyword) then setting o to null will also set anObject to null. Passing by reference forces you to explicitly declare the type of the COM object passed in to your ReleaseCOM() method. This means you must implement a ReleaseCOM() for every type of Trim COM object that you might conceivably use – this is too much work!

Breaking out of Loops

Do not immediately break out of loops using the “break” keyword as this may bypass any code that releases the Trim COM objects that are instantiated within the loop. Instead, use the [more tedious] method of setting a breakout flag, like in the example below.

Database db = null;
RecLocations contacts = null;
Record record = null;
Location location = null;

RecLocation contact = null;
Location tmpLoc = null;

try
{
    db = new DatabaseClass();
    db.Id = "BC";
    db.Connect();

    record = db.GetRecord("06/1");
    location = db.GetLocation("zaki");

    contacts = record.RecLocations;

    bool breakout = false;

    for(int i=0;i<contacts.Count;i++)
    {
        contact = contacts.Item(i); /*remember to release this COM object that just got instantiated within the loop before you go to the
 next iteration. Otherwise, if you have 1000 contacts, only the last will be released and the rest will be a memory leak*/

        if(contact.RecLocType == rlRecordLocationType.rlContact && contact.SubType == ctContactType.ctOther)
        {
            tmpLoc = contact.Location;

            if(tmpLoc.Uri.Equals(location.Uri))
            {
                contact.Delete();
                breakout = true;
            }
				
            ReleaseCOMObject(tmpLoc);
            tmpLoc = null;
        }
			
        ReleaseCOMObject(contact);
        contact = null;

        if(breakout) 
            break; //<<-- BREAK out of LOOP
    }
    
    record.Save();			
}
catch(Exception ex)
{
    Console.WriteLine("Error: " + ex.Message);
}
finally
{
    if ( db!= null)
    {
        db.Disconnect();
        ReleaseCOMObject(db);
        db = null;
    }

    if (contacts != null) 
    {
        ReleaseCOMObject(contacts);
        contacts = null;
    }

    if (record != null)
    {
        ReleaseCOMObject(record);
        record = null;
    }
		
    if (location != null)
    {	
        ReleaseCOMObject(location);
        location = null;
    }
}

Reference Variables

Every COM object must be assigned to a ReferenceVariable so that it can be released later to avoid Marshalling Errors.

Database db = new Database();
db.Connect();
Console.WriteLine(db.CurrentUser.Full_Name);

ReleaseCOM(db);
//you can’t release the Location object that got instantiated when you called db.CurrentUser above

The followinf sample allows the ReleaseCOM for Location to be executed:

Database db = new Database();
db.Connect();
Location loc = db.CurrentUser;
Location position = me.Position;
Location branch = position.Unit;

Console.WriteLine(loc.FullName);

...

ReleaseCOM(loc);
ReleaseCOM(position);
ReleaseCOM(branch);
ReleaseCOM(db);

COM Objects in Loops

For every iteration through a Trim Collection, call ReleaseCOM to release the COM object that comes into existence within the iteration. Below, for every value of “i”, contacts.Item(i) will create a new COM object in memory and this is then assigned to the reference variable contact. If you don’t release the object in memory reference by the variable contact, then the next iteration will assign a new COM object to the same variable, leaving the previous COM object in memory that is not released.

for(int i=0;i<contacts.Count;i++)
{
    contact = contacts.Item(i);
    if(contact.RecLocType == rlRecordLocationType.rlContact && contact.SubType == ctContactType.ctOther)
    {
        tmpLoc = contact.Location;

        if(tmpLoc.Uri.Equals(location.Uri))
        {
            contact.Delete();
            breakout = true;
        }

        ReleaseCOMObject(tmpLoc);
        tmpLoc = null;
    }

    ReleaseCOMObject(contact);
    contact = null;

    if(breakout) 
        break;
}

Make Your Class Implement IDisposable

Wrap your COM objects in a class that implements IDisposable, so that when that wrapper class goes out of scope, the CLR will automatically call your Dispose() method where the COM object will be released.

This is especially useful in web applications where you want to keep a database connection in the session cache. If a user explicitly forgets to logout, you can wait for the session to time out. When it does, objects stored in the session cache will be disposed of by having its Dispose() method automatically called (if it implements IDisposable).

public class foo : IDisposable
{
    int a, b;
    Database db = null;

    public foo()
    {
        //To initiate 
        a = 0;
        b = 0;
        db = new DatabaseClass();
    }

    public ~foo()	/* DONT put code for releasing COM objects in the destructor. The GC may not call this destructor when 
the object goes out of scope */
    {	
        a = null;
        b = null;
    }

    public finalize() /* DONT put code for releasing COM objects here.*/
    {
    }

    public void Dispose()
    {
        //Put Cleanup Codes for COM objects here
        if(db != null)
        {
            db.Disconnect();
            ReleaseCOM(db);
            db = null;
        }
    }
}

References