Common Pitfalls of COM Interoperability in .Net

Introduction

Introduction

 

COM Interoperability is an important feature of .Net; this allows us to use COM objects in .Net and vice versa. This feature helps us to develop new applications still making use of old components that were written as COM. What more even to interact with MS Office applications (ex - Office 2003) we need to use interoperability.

 

Interoperability is a powerful feature, with power comes responsibility. Code written in .NET runs under managed environment, where as COM objects run as unmanaged components. There are several common pitfalls that the .Net developers using interoperability have to be aware of. This would save the valuable development time that would otherwise be wasted to debug the weird issues encountered using interoperability.

let us look at the common pitfalls of using COM interoperability. This discussion focuses mainly on treatment of COM objects in our code.

 

Scenario 1: Passing COM objects between functions

 

It is not recommended to pass the COM objects between functions. It would be always better if the COM objects are created in local scope and release immediately.

However if there is requirement to pass the COM object between functions, one must be aware of the following scenario.

 

If COM objects are passed

a) The received objects are not released.

b) Creator always releases the objects.

 

Look at the following code snippet

 

Wrong Usage:

 

public partial class SalesDemo : Form

{

   public SalesDemo()

   {

 
      InitializeComponent();

 


      Invoice l_oInvoice = new Sales.InvoiceClass();

 

      WrongParameterHandeling(l_oInvoice);

      l_oInvoice.ProductObject.GetProductCode();

   }

 

   public void WrongParameterHandeling(Invoice p_oInvoice)

   {

      try

      {

         //Use ComObject

 
      }

      finally

      {

  System.Runtime.InteropServices.Marshal.ReleaseComObject(p_oInvoice);

      }

   }

}

 

The code when executed throws up an exception

 

System.Runtime.InteropServices.InvalidComObjectException was unhandled

Message="COM object that has been separated from its underlying RCW cannot be

used."

 

Text Box: Reason

 

 

This is one of the most common exceptions with COM interoperability; this occurs when we release the COM objects and try to make a call to the COM object through the wrapper class.

 

In the above shown snippet the function WrongParameterHandeling has released the COM object, hence there after references of the Wrapper classes will no longer hold the reference to the COM object and the statement

 

l_oInvoice.ProductObject.GetProductCode();

 

throws the Invalid COM object exception.

Correct Usage:

 

namespace COMInteropDemo

{

   public partial class SalesDemo : Form

   {

      public SalesDemo()

      {

         InitializeComponent();

 

         Invoice l_oInvoice = new Sales.InvoiceClass();

         CorrectParmHandeling(l_oInvoice);

Text Box: COM Object released by creator          l_oInvoice.ProductObject.GetProductCode();

 


System.Runtime.InteropServices.Marshal.ReleaseComObject(l_oInvoice);

      }

 

      public void CorrectParmHandeling(Invoice p_oInvoice)

      {

        try

        {

           //Use ComObject

        }

        catch(Exception e)

        {

 

        }

 

      }

  }

}

 

Scenario 2: Events from COM objects

 

If events are subscribed from COM objects make sure they are unsubscribed in relevant functions.

 

public partial class SalesDemo : Form

{

 

   public SalesDemo()

   {

      InitializeComponent();

 

      Invoice l_oInvoice = new Sales.InvoiceClass();

      CorrectParmHandeling(l_oInvoice);

Text Box:  COM Object Created      l_oInvoice.ProductObject.GetProductCode();

 

Text Box: Even subscribed, G_oProduct is a Global object and has life even after the Form is out of scopeG_oProdcut.PriceUpdated += new __Product_PriceUpdatedEventHandler(G_oProdcut_PriceUpdated);

 

System.Runtime.InteropServices.Marshal.ReleaseComObject(l_oInvoice);

   }

 


Text Box:  Subscribe the COM event   void l_oProdcut_PriceUpdated(ref short NewPrice)

   {

      //Implement logic

   }

 

When we run the above code the works as expected, but once we move out of the form and form goes out of scope, un expected results are got next time the PriceUpdated event gets fired.

 

 

 


Even listeners remain in memory even after they are out of scope (but not claimed by GC) and react to the events when they occur leading to unexpected results.

 

 

Unsubscribe all the com events in relevant functions. In case of a form same can be done in Dispose method.

 

Scenario 3: intermediate COM objects in the code

It is not a good practice to use intermediate COM objects in code, one can generally pull it off in standalone applications but it the same is a service, and this could have -ve impact.

 

 

Wrong Usage:

 

l_oInvoice.ProductObject.GetProductCode();

 

 

 

 


A reference to the intermediate object is created in .Net. This object cannot be released as no reference is available. Releasing the main object does not release the intermediate object. Ex releasing l_oInvoice does not release ProductObject.

Correct Usage:

 

Product l_oProdcut = l_oInvoice.ProductObject;

l_oProdcut.GetProductCode();

System.Runtime.InteropServices.Marshal.ReleaseComObject(l_oInvoice);

System.Runtime.InteropServices.Marshal.ReleaseComObject(l_oProdcut);

 

Scenario 4: illegal memory access exception

 

When developing COM rich applications in .Net it is possible that one faces illegal memory access exception,

"Attempted to read or write protected memory. This is often an indication that other memory is corrupt"

 

Even though there may be many reasons for this error including an error on the COM side itself, one of the possible reasons is related to the treatment of COM objects in .NET.

 

Consider the following scenario of composition; we have a COM collection object Invoices, which holds a collection of Invoice objects.

 

Invoices object has a method reload which reloads the data of the selected child from the database. Invoice object has a refresh method which requests the reload.

 

Even though the example takes a collection to show the concept the same is applicable to any kind of compositions with dependencies.



Consider the below function which takes Invoice number as the parameter and returns an Invoice object.

 
Wrong Usage:

 

public Invoice GetInvoices(string p_iInvoiceNumber)

{

   Invoices l_oInvoices = new Invoices();

   Invoice l_oInvoiceToReturn;

 

   //Code to populate the Invoices from the database

 

   for (int i = 0; i < l_oInvoices.count; i++)

   {

      if (l_oInvoices[i].InvoiceNumber == p_iInvoiceNumber)

      {

         l_oInvoiceToReturn = l_oInvoices[i];

         break;

      }

   }

   Marshal.ReleaseComObject(l_oInvoices);

   return l_oInvoiceToReturn;

}

 

The above function is returning the object, but it releases the Invoices collection object. The using of the Invoice object will not create any error as long as we do not try to refresh the data (which in this case is assumed to be through the collection).

Correct usage:

 

public Invoice GetInvoices(string p_iInvoiceNumber, Invoices l_oInvoices)

{

   Invoice l_oInvoiceToReturn;

 

   for (int i = 0; i < l_oInvoices.count; i++)

   {

      if (l_oInvoices[i].InvoiceNumber == p_iInvoiceNumber)

      {

        l_oInvoiceToReturn = l_oInvoices[i];

        break;

      }

   }

   return l_oInvoiceToReturn;

}

 

The best way would be to pass the collection object to the function and return the Invoice object. And let the calling function which wants to use the Invoice object take care of the collection as well.

 

 

Conclusion

 

Debugging applications is as much a part of development as implementing new code. Debugging COM interoperability applications could sometimes be complex, especially when error messages are not specific. I have tried to share few such scenarios that I came across during development hoping to save some time for my co-developers who work on similar applications.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Event Date: April 15, 2014 The ability to effectively set sales goals, assign quotas and territories, bring new people on board and quickly make adjustments to the sales force is often crucial to success--and to the field experience! But for sales operations leaders, managing the administrative processes, systems, data and various departments to get it all right can often be difficult, inefficient and manually intensive. Register for this webinar and learn how you can: Align sales goals, quotas and …

  • With 81% of employees using their phones at work, companies have stopped asking: "Is corporate data leaking from personal devices?" and started asking: "How do we effectively prevent corporate data from leaking from personal devices?" The answer has not been simple. ZixOne raises the bar on BYOD security by not allowing email data to reside on the device. In addition, Zix allows employees to maintain complete control of their personal device, therefore satisfying privacy demands of valued employees and the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds