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

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Not all enterprise applications are created equal. Sophisticated applications need developer support but other more basic apps do not. With the right tools, everyone is a potential app developer with ideas and a perspective to share. Trends such as low-code development and model driven development are fundamentally changing how and who creates applications. Is your organization ready? Read this report and learn: The seven personas of enterprise app delivery How application ownership is spreading to the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds