Click here to Skip to main content
15,885,366 members
Articles / Programming Languages / C#

SafeCOMWrapper - Managed Disposable Strongly Typed safe wrapper to late bound COM

Rate me:
Please Sign up or sign in to vote.
4.90/5 (58 votes)
19 Sep 2005CPOL10 min read 452.7K   2.1K   132  
Make version independent COM wrapper using late bound calls yet providing strongly type and disposable interfaces. A version independent managed Outlook Automation Library.
using System;
using System.Diagnostics;
using System.Runtime.Remoting.Proxies;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting;
using System.Reflection;

namespace SimpleOfficeWrapper
{
	public interface Application : IDisposable
	{
		string Name { get; }
		Explorer ActiveExplorer();
		void Quit();
	}
	public interface Explorer : IDisposable
	{
		string Caption { get; }
		void Close();
		void Display();
		void Activate();
	}



	public class DisposableCOMProxy : RealProxy
	{
		public object COM;

		/// <summary>
		/// We will be using late bound COM operations. The COM object
		/// is created from the Prog ID instead of CLSID which makes it
		/// a version independent approach to instantiate COM.
		/// </summary>
		/// <param name="progID">Prog ID e.g. Outlook.Application</param>
		/// <returns></returns>
		private static object CreateCOM( string progID )
		{
			// Instantiate the COM object using late bound
			Type comType = Type.GetTypeFromProgID( progID, true );
			return Activator.CreateInstance( comType );
		}

		public static IDisposable Create( string progID, Type interfaceType )
		{	
			object theCOM = CreateCOM( progID );
			DisposableCOMProxy wrapper = new DisposableCOMProxy( theCOM, interfaceType );
			return wrapper.GetTransparentProxy() as IDisposable;
		}

		public DisposableCOMProxy( object theCOM, Type interfaceType ) :base( interfaceType )
		{
			this.COM = theCOM;
		}

		public override IMessage Invoke(IMessage msg)
		{
			IMethodCallMessage callMessage = msg as IMethodCallMessage;

			object returnValue = null;

			MethodInfo method = callMessage.MethodBase as MethodInfo;					
			
			// We intercept all method calls on the interface and delegate the method
			// call to the COM reference.
			// Only exception is for "Dispose" which needs to be called on this class
			// in order to release the COM reference. COM reference does not have dispose
			if( method.Name == "Dispose" )
			{
				this.Release();
			}
			else
			{

				object invokeObject = this.COM;
				Type invokeType = this.COM.GetType();

				// Get Property called: Retrieve property value
				if( method.Name.StartsWith("get_") )
				{
					string propertyName = method.Name.Substring(4);
					returnValue = invokeType.InvokeMember( propertyName, BindingFlags.GetProperty, null,
						invokeObject, callMessage.InArgs );
				}
					// Set Property Called: Set the property value
				else if( method.Name.StartsWith("set_") )
				{
					string propertyName = method.Name.Substring(4);						
					returnValue = invokeType.InvokeMember( propertyName, BindingFlags.SetProperty, null,
						invokeObject, callMessage.InArgs );
				}
					// Regular method call
				else
				{
					returnValue = invokeType.InvokeMember( method.Name, BindingFlags.InvokeMethod, null,
						invokeObject, callMessage.Args );
				}

				// Now check if the method return value is also an interface. if it is an 
				// interface, then we are interested to intercept that too
				if( method.ReturnType.IsInterface && null != returnValue )
				{	
					// Return a intercepting wrapper for the com object
					DisposableCOMProxy proxy = new DisposableCOMProxy( returnValue, method.ReturnType );
					returnValue = proxy.GetTransparentProxy();
				}
				else if( method.ReturnType.IsEnum && null != returnValue )
				{
					// Convert to proper enum type
					returnValue = Enum.Parse(method.ReturnType, returnValue.ToString());
				}
			}

			// Construct a return message which contains the return value 
			ReturnMessage returnMessage = new ReturnMessage( returnValue, null, 
				0, callMessage.LogicalCallContext,
				callMessage );

			return returnMessage;
		}

		/// <summary>
		/// Safely release the COM object
		/// </summary>
		private void Release()
		{
			if( null == this.COM ) return;

			while( Marshal.ReleaseComObject( this.COM ) > 0 );
			this.COM = null;

			Debug.WriteLine( "COM released successfully" );
		}

		~DisposableCOMProxy()
		{
			this.Release();
		}

		public static void Main()
		{
			// Instantiate Outlook.Application and wrap the COM with the Application
			// interface so that we have a strongly type managed wrapper to late bound 
			// COM
			using( Application app = (Application)DisposableCOMProxy.Create( "Outlook.Application", 
					   typeof( Application ) ) )
			{
				Debug.WriteLine( app.Name );

				using( Explorer explorer = app.ActiveExplorer() )
				{
					Debug.WriteLine( explorer.Caption );
				}
			}
		}
	}

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Architect BT, UK (ex British Telecom)
United Kingdom United Kingdom

Comments and Discussions