Iterating through the contents of the ROT (running objects table) using C#

Back in the day I had to do quite a bit of COM / .Net interop. For some reason (and I cannot remember what it was), I need to be able to enumerate the COM running objects table (ROT). I think it was to do with createboject/getobject and trying to synthesis a singleton for an exe.

The System.Runtime.InteropServices.Marshal.GetActiveObject method will return an existing instance of application registered in the Running Objects Table (ROT) by matching the prog id (or moniker) to an entry in this table.

Note, Office applications do not register themselves if another instance is already in the ROT because the moniker for itself is always the same, and cannot be distinguished. This means that you cannot attach to any instance except for the first instance. However, because Office applications also register their documents in the ROT, you can successfully attach to other instances by iterating the ROT looking for a specific document, and attaching to this document to get the Application object from this document.

The source code below contains a ROTHelper class, which among other things, demonstrates how to iterate through the ROT. Note, once you have the desired application object you will either need to use reflection or cast to the relevant COM interface to call any methods.

using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Collections;

#region TestROT Class

/// <summary>
/// Test ROT class showing how to use the ROTHelper.
/// </summary>
class TestROT
{
	/// <summary>
	/// The main entry point for the application.
	/// </summary>
	[STAThread]
	static void Main(string[] args)
	{
		// Iterate through all the objects in the ROT
		Hashtable runningObjects = ROTHelper.GetActiveObjectList(null);
		// Display the object ids
		foreach( DictionaryEntry de in runningObjects ) 
		{
			string progId = de.Key.ToString();
			if ( progId.IndexOf("{") != -1 ) 
			{
				// Convert a class id into a friendly prog Id
				progId = ROTHelper.ConvertClassIdToProgId( de.Key.ToString() ) ;
			}
			Console.WriteLine( progId );
			object getObj = ROTHelper.GetActiveObject(progId);
			if ( getObj != null ) 
			{
				Console.WriteLine( "Fetched: " + progId );
			}
			else
			{
				Console.WriteLine( "!!!!!FAILED TO fetch: " + progId );
			}
		}
		Console.ReadLine();
	}
}
#endregion TestROT Class

#region ROTHelper Class

/// <summary>
/// The COM running object table utility class.
/// </summary>
public class ROTHelper
{
	#region APIs

	[DllImport("ole32.dll")]  
	private static extern int GetRunningObjectTable(int reserved, 
		out UCOMIRunningObjectTable prot); 

	[DllImport("ole32.dll")]  
	private static extern int  CreateBindCtx(int reserved, 
		out UCOMIBindCtx ppbc);

	[DllImport("ole32.dll", PreserveSig=false)]
	private static extern void CLSIDFromProgIDEx([MarshalAs(UnmanagedType.LPWStr)] string progId, out Guid clsid);

	[DllImport("ole32.dll", PreserveSig=false)]
	private static extern void CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] string progId, out Guid clsid);

	[DllImport("ole32.dll")]
	private static extern int ProgIDFromCLSID([In()]ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)]out string lplpszProgID);

	#endregion

	#region Public Methods
	
	/// <summary>
	/// Converts a COM class ID into a prog id.
	/// </summary>
	/// <param name="progID">The prog id to convert to a class id.</param>
	/// <returns>Returns the matching class id or the prog id if it wasn't found.</returns>
	public static string ConvertProgIdToClassId( string progID )
	{
		Guid testGuid;
		try
		{
			CLSIDFromProgIDEx(progID, out testGuid);
		}
		catch
		{
			try
			{
				CLSIDFromProgID(progID, out testGuid);
			}
			catch
			{
				return progID;
			}
		}
		return testGuid.ToString().ToUpper();
	}

	/// <summary>
	/// Converts a COM class ID into a prog id.
	/// </summary>
	/// <param name="classID">The class id to convert to a prog id.</param>
	/// <returns>Returns the matching class id or null if it wasn't found.</returns>
	public static string ConvertClassIdToProgId( string classID )
	{
		Guid testGuid = new Guid(classID.Replace("!",""));
		string progId = null;
		try
		{
			ProgIDFromCLSID(ref testGuid, out progId);
		}
		catch (Exception)
		{
			return null;
		}
		return progId;
	}

	/// <summary>
	/// Get a snapshot of the running object table (ROT).
	/// </summary>
	/// <returns>A hashtable mapping the name of the object in the ROT to the corresponding object
	/// <param name="filter">The filter to apply to the list (nullable).</param>
	/// <returns>A hashtable of the matching entries in the ROT</returns>
	public static Hashtable GetActiveObjectList(string filter)
	{
		Hashtable result = new Hashtable();

		int numFetched;
		UCOMIRunningObjectTable runningObjectTable;   
		UCOMIEnumMoniker monikerEnumerator;
		UCOMIMoniker[] monikers = new UCOMIMoniker[1];

		GetRunningObjectTable(0, out runningObjectTable);    
		runningObjectTable.EnumRunning(out monikerEnumerator);
		monikerEnumerator.Reset();          

		while (monikerEnumerator.Next(1, monikers, out numFetched) == 0)
		{     
			UCOMIBindCtx ctx;
			CreateBindCtx(0, out ctx);     
        
			string runningObjectName;
			monikers[0].GetDisplayName(ctx, null, out runningObjectName);

			object runningObjectVal;  
			runningObjectTable.GetObject( monikers[0], out runningObjectVal); 
			if ( filter == null || filter.Length == 0 || filter.IndexOf( filter ) != -1 ) 
			{
				result[ runningObjectName ] = runningObjectVal;
			}
		} 

		return result;
	}

	/// <summary>
	/// Returns an object from the ROT, given a prog Id.
	/// </summary>
	/// <param name="progId">The prog id of the object to return.</param>
	/// <returns>The requested object, or null if the object is not found.</returns>
	public static object GetActiveObject(string progId)
	{
		// Convert the prog id into a class id
		string classId = ConvertProgIdToClassId( progId );

		UCOMIRunningObjectTable prot = null;
		UCOMIEnumMoniker pMonkEnum = null;
		try
		{
    		int Fetched = 0;
			// Open the running objects table.
			GetRunningObjectTable(0, out prot);
			prot.EnumRunning(out pMonkEnum);
			pMonkEnum.Reset();
			UCOMIMoniker[] pmon = new UCOMIMoniker[1];

			// Iterate through the results
			while (pMonkEnum.Next(1, pmon, out Fetched) == 0)
			{
				UCOMIBindCtx pCtx;

				CreateBindCtx(0, out pCtx);

				string displayName;
				pmon[0].GetDisplayName(pCtx, null, out displayName);
				Marshal.ReleaseComObject(pCtx);
				if ( displayName.IndexOf( classId ) != -1 )
				{
					// Return the matching object
					object objReturnObject;
					prot.GetObject(pmon[0], out objReturnObject);
					return objReturnObject;
				}
			}
			return null;
		}
		finally
		{
			// Free resources
			if (prot != null )
				Marshal.ReleaseComObject( prot);
			if (pMonkEnum != null )
				Marshal.ReleaseComObject ( pMonkEnum );
		}
	}

	#endregion
}

#endregion

Leave a Reply

Your email address will not be published. Required fields are marked *