Click here to Skip to main content
Click here to Skip to main content

How to Release COM Interop Objects so the Called Program Can Exit

By , 29 Mar 2013
 

Introduction

If COM Interop objects are not properly released in your .NET code, the program that your .NET program is communicating with via COM Interop will remain in the Windows operating system task list without a visible window. You can see it in the Details tab of Task Manager.

Background

The common language runtime exposes COM objects through a proxy called the Runtime Callable Wrapper (RCW). To a .NET application, a RCW looks like an ordinary .NET object. It handles calls between a .NET client and a COM object (e.g. The EXCEL.EXE application and objects within EXCEL.EXE) marshaling arguments and return values between the .NET client and the COM object; converting them when necessary.

For each COM object, the .NET runtime creates a single RCW regardless of the number of references to that object. A reference count is incremented each time a new reference is made to the RCW of an instantiated COM object. Calling a "Quit" or "Exit" method followed by a "Dispose" method on a COM object will not free up all of the references. The example code in this tip will ensure that all references are released so that the called COM program will end properly.

Coding Model for COM Interop

  1. Instantiate COM objects just before they are needed.
  2. Use ReleaseComObject(obj) to release objects as soon as they are not needed.
  3. Repeat ReleaseComObject(obj) on each object until ReleaseComObject(obj) returns 0.
  4. Call ReleaseComObject on the most recently created objects first.
  5. Use the .NET Garbage Collection methods to force garbage collection.

COM Interop Example

My original example is VB .NET code. I used the developerFusion Convert VB .NET to C# tool to convert my VB .NET code to the C# code included in this tip.

VB Example

Dim strFilename As String = "<the>"
' Declare a COM object
Dim oApp As Excel.Application 
' Start Excel
oApp = DirectCast(CreateObject("Excel.Application"), Excel.Application)
oApp.Visible = True
Application.DoEvents()
' Open file
oApp.Workbooks.Open(strNewFilename, Excel.XlUpdateLinks.xlUpdateLinksNever)
Application.DoEvents()
' Run the CreateInventoryWorksheets Macro
oApp.Run("CreateInventoryWorksheets")
Application.DoEvents()
' Macro is finished
' Close the file
oApp.ActiveWorkbook.Close(True)
Application.DoEvents()
' Tell Excel to quit
oApp.Quit()
Application.DoEvents()
'
'
' This sample has only one COM object declared. 
' For programs with multiple COM objects declared, repeat the following three 
' lines of code for each COM object in reverse order of creation.
' 
While System.Runtime.InteropServices.Marshal.ReleaseComObject(oApp) <> 0
    Application.DoEvents()
End While
' Set each COM Object to Nothing
oApp = Nothing 
'
' After all of the COM objects have been released and set to Nothing, do the following:
GC.Collect() ' Start .NET CLR Garbage Collection
GC.WaitForPendingFinalizers() ' Wait for Garbage Collection to finish
</the>

C# Example

string strFilename = "";
// Declare a COM object
Excel.Application oApp = default(Excel.Application);
// Start Excel
oApp = (Excel.Application)Interaction.CreateObject("Excel.Application");
oApp.Visible = true;
Application.DoEvents();
// Open file
oApp.Workbooks.Open(strNewFilename, Excel.XlUpdateLinks.xlUpdateLinksNever);
Application.DoEvents();
// Run the CreateInventoryWorksheets Macro
oApp.Run("CreateInventoryWorksheets");
Application.DoEvents();
// Macro is finished
// Close the file
oApp.ActiveWorkbook.Close(true);
Application.DoEvents();
// Tell Excel to quit
oApp.Quit();
Application.DoEvents();
//
//
// This sample has only one COM object declared. 
// For programs with multiple COM objects declared, repeat the following three 
// lines of code for each COM object in reverse order of creation.
// 
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(oApp) != 0) {
	Application.DoEvents();
}
// Set each COM Object to null
oApp = null;
//
// After all of the COM objects have been released and set to null, do the following:
GC.Collect(); // Start .NET CLR Garbage Collection
GC.WaitForPendingFinalizers(); // Wait for Garbage Collection to finish

Points of Interest

Below are links to several articles about COM Interop. Thanks to those authors for their fine articles.

History

  • 29 March 2013 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Mike Meinz
Retired
United States United States
Member
I’m retired. When I started my career, programming projects consisted of plugging wires into plug boards to create punch card processing applications to be run on electrical accounting machine like the IBM 402, 407, 085, 088, 514, 519, etc. From there, I moved to writing SPS and Autocoder applications on an IBM 1401 with 4K of memory eventually upgraded to 16K of memory. After many years of migrating my skills to various languages on various hardware platforms, I became an Information Technology Director where I didn’t need to program anymore. So, starting in 1996, I volunteered my time with a local community cable television organization and built some applications to help them run their operations. Originally in Clipper Summer 1987 and later Clipper 5.2, I migrated and enhanced those applications to VB .NET 2003 almost ten years ago. I retired in 2010 and since then have done a few enhancements but nothing much. Now, my VB .NET 2003 Solution is to be upgraded to VB .NET 2012 so that it can run on 64-bit computers and interact with Microsoft Office 2010. I was pleasantly surprised to learn that my VB .NET 2003 Solution and all of the Projects within it were able to be opened by Visual Studio 2012. I had to correct more than 400 warnings about uninitialized variables that were initialized in “If” statement blocks or “Try” statement blocks. One error caused by an item that had been removed from the .NET Framework was easily corrected. I changed the use of a System.Net.Dns property from GetHostByName().AddressList to GetHostEntry().AddressList.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5 PinmemberVitorHugoGarcia8 Apr '13 - 5:43 
GeneralMy vote of 5 PinmemberJohn Atten29 Mar '13 - 16:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130513.1 | Last Updated 29 Mar 2013
Article Copyright 2013 by Mike Meinz
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid