ComReference(Of T) — A type safe reference to a COM object

Problem

Calling Marshal.ReleaseComObject and Marshal.FinalReleaseComObject can improve the performance of .NET applications that make heavy use of COM objects.

Unfortunately neither method is called deterministically. Normally you need to wrap the COM object usage in a Try/Finally block and call Marshal.ReleaseComObject from the Finally block if you want to deterministically call these methods. This unfortunately can lead to some code duplication.

Solution

Create an object that implements IDisposable.Dispose allowing one to use the new Using Statement available in Visual Basic 2005

Discussion

ComReference(Of T) calls Marshal.FinalReleaseComObject in its implementation of the IDisposable.Dispose method. Thus ensuring that Marshal.FinalReleaseComObject will be called deterministically in a manner. Marshal.FinalReleaseComObject ensures that the COM object is fully released, without needing a loop around calls to Marshal.ReleaseComObject.

ComReference(Of T) uses Generics to offer a type safe Target property that is the type of the COM reference it contains.

NOTE: The major caveat of using ComReference(Of T) is that you need to use the ComReference(Of T).Target property to get to the underlying COM object. Nesting a With statement inside the Using statement can simplify this...

Example

Example 1

Visual Basic 2005
 
Imports Outlook = Microsoft.Office.Interop.Outlook

    Using appOutlook As New ComReference(Of Outlook.Application)(New Outlook.Application)
        Using ns As New ComReference(Of Outlook.NameSpace)(appOutlook.Target.GetNamespace("MAPI"))
            With ns.Target
                .Logon()
                ' Use Outlook here...
                .Logoff()
            End With
        End Using 
    End Using

Source

ComReference(Of T)

Visual Basic 2005
 
'
'   Copyright © 2005, Jay B. Harlow, All Rights Reserved.
'
Option Strict On
Option Explicit On

Imports System.Runtime.InteropServices

Public Structure ComReference(Of T As Class)
    Implements IDisposable
 
    Private ReadOnly m_target As T

    Public Sub New(ByVal target As T)
        If target Is Nothing Then Throw New ArgumentNullException("target")
        If Not Marshal.IsComObject(target) Then Throw New ArgumentException("Expecting a COM object", "target")
        If TypeOf target Is IDisposable Then Throw New ArgumentException("Not expecting a Disposable object", "target")
        m_target = target
    End Sub

    Public Sub New(ByVal target As Object)
        MyClass.New(DirectCast(target, T))
    End Sub

    Public ReadOnly Property Target() As T
        Get
            Return m_target
        End Get
    End Property

    Public Sub Dispose() Implements IDisposable.Dispose
        If Marshal.IsComObject(m_target) Then
            If Marshal.FinalReleaseComObject(m_target) <> 0 Then
                ' TODO: Decide if we want to throw an exception!
            End If
        End If
    End Sub

End Structure

See Also