Andrei Smolin

HowTo: get the actual type name behind System.__ComObject with Visual C# or VB.NET

Developers accessing Application.Selection and Application.ActiveSheet in Excel or Explorer.Selection.Item(i), Folder.Items(j), Inspector.CurrentItem in Outlook do know the notorious “System.__ComObject” string. It indicates that the actual type of the COM object is unknown.

In HOW TO: Check the Type of a COM Object (System.__ComObject) with Visual C# .NET, Microsoft suggests this approach:

C#:

private void ButtonClick(object sender, System.EventArgs e)
{
    object o = oExcel.Selection;
 
    //Display a message about the selection type in Excel.
    if ((o as Excel.Range) != null)
    {
        textBox1.Text = "Selection is Excel.Range";
    }
    else if ((o as Excel.Rectangle) != null)
    {
        textBox1.Text = "Selection is Excel.Rectangle";
    }
    else if ((o as Excel.ChartObject) != null)
    {
        textBox1.Text = "Selection is Excel.ChartObject";
    }
    else if ((o as Excel.ChartArea) != null)
    {
        textBox1.Text = "Selection is Excel.ChartArea";
    }
    // ... There are many additional Selection types you could check for if needed ...
    else
    {
        textBox1.Text = "Selection is Unknown";
    }
}

This code is simple if only you know what object types are selectable in the Excel UI. If you are not familiar with the Excel object model, you would prefer to have the type name of the selected object in a debugging message. Do you remember how they do this in the old good VBA:

VBA:

MsgBox("The selection object type is " & TypeName(Selection))

In another article named HOWTO: Know the actual type behind a System.__ComObject type returned by an .Object property the proposed solution is to call Microsoft.VisualBasic.Information.TypeName(); this requires referencing Microsoft.VisualBasic.dll.

The solution below gets the actual type name of such an object in three steps:

  1. Cast the object to the IDispatch type.
  2. Get the ITypeInfo interface via IDispatch.GetTypeInfo().
  3. Get the type name using ITypeInfo.GetDocumentation().

Below is the code; I posted it previously on Microsoft forums. Be accurate; release every COM object you create in your code!

C#:

using System;
using System.Runtime.InteropServices;
using ComTypes = System.Runtime.InteropServices.ComTypes;
 
namespace ComUtils
{
    public class ComHelper
    {
        /// <summary>
        /// Returns a string value representing the type name of the specified COM object.
        /// </summary>
        /// <param name="comObj">A COM object the type name of which to return.</param>
        /// <returns>A string containing the type name.</returns>
        public static string GetTypeName(object comObj)
        {
 
            if (comObj == null)
                return String.Empty;
 
            if (!Marshal.IsComObject(comObj))
                //The specified object is not a COM object
                return String.Empty;
 
            IDispatch dispatch = comObj as IDispatch;
            if (dispatch == null)
                //The specified COM object doesn't support getting type information
                return String.Empty;
 
            ComTypes.ITypeInfo typeInfo = null;
            try
            {
                try
                {
                    // obtain the ITypeInfo interface from the object
                    dispatch.GetTypeInfo(0, 0, out typeInfo);
                }
                catch (Exception ex)
                {
                    //Cannot get the ITypeInfo interface for the specified COM object
                    return String.Empty;
                }
 
                string typeName = "";
                string documentation, helpFile;
                int helpContext = -1;
 
                try
                {
                    //retrieves the documentation string for the specified type description 
                    typeInfo.GetDocumentation(-1, out typeName, out documentation,
						out helpContext, out helpFile);
                }
                catch (Exception ex)
                {
                    // Cannot extract ITypeInfo information
                    return String.Empty;
                }
                return typeName;
            }
            catch (Exception ex)
            {
                // Unexpected error
                return String.Empty;
            }
            finally
            {
                if (typeInfo != null) Marshal.ReleaseComObject(typeInfo);
            }
        }
    }
 
    /// <summary>
    /// Exposes objects, methods and properties to programming tools and other
	/// applications that support Automation.
    /// </summary>
    [ComImport()]
    [Guid("00020400-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IDispatch
    {
        [PreserveSig]
        int GetTypeInfoCount(out int Count);
 
        [PreserveSig]
        int GetTypeInfo(
            [MarshalAs(UnmanagedType.U4)] int iTInfo,
            [MarshalAs(UnmanagedType.U4)] int lcid,
            out ComTypes.ITypeInfo typeInfo);
 
        [PreserveSig]
        int GetIDsOfNames(
            ref Guid riid,
            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)]
			string[] rgsNames,
            int cNames,
            int lcid,
            [MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
 
        [PreserveSig]
        int Invoke(
            int dispIdMember,
            ref Guid riid,
            uint lcid,
            ushort wFlags,
            ref ComTypes.DISPPARAMS pDispParams,
            out object pVarResult,
            ref ComTypes.EXCEPINFO pExcepInfo,
            IntPtr[] pArgErr);
    }
}

And this is how you get the type name of the Excel selection:
C#:

object selection = ExcelApp.Selection;
            if (selection != null)
            {
                string typeName = ComHelper.GetTypeName(selection);
                Debug.Print(typeName);
                Marshal.ReleaseComObject(selection);
            }

Good luck!

Available downloads:

This sample COM Add-in for Excel was developed using Add-in Express for Office and .net:

C# sample COM add-in
VB.NET sample COM add-in

14 Comments

  • Matt says:

    Can you not do this?

    In C# add a reference to Microsoft.VisualBasic

    private void whatType(object obj)
    {
    System.Diagnostics.Debug.WriteLine(Microsoft.VisualBasic.Information.TypeName(obj));
    }

  • Andrei Smolin (Add-in Express Team) says:

    Thank you for posting, Matt!

    I pointed out that way :)
    The blog is for C# purists :)

  • Daren says:

    Very helpful, but I am still stuck. I have pasted a clipart picture into an excel spreadsheet. In my Add-in, I am trying to identify the selected object when the picture is selected. Using your ComHelper code, I have confirmed that the object is a “Picture”. But I cannot “cast” this selection as an Excel.Picture. The Picture interface says “This interface supports the .NET Framework infrastructure and is not intended to be used directly from your code.”

    How do I create an Excel.Picture object?

  • Andrei Smolin (Add-in Express Team) says:

    Daren,

    Please check if the add-in described at https://www.add-in-express.com/creating-addins-blog/excel-shapes-events/ works for you. The add-in is provided with the source code; I suppose the ParseSelection method is what you are looking for.

  • Graham says:

    I can’t get C# 2010 express to recognize IDispatch

    What using or reference should I add?

  • Andrei Smolin (Add-in Express Team) says:

    It is desclared in the code. Find the string “interface IDispatch” in your code or on this page.

  • Graham says:

    Ah yes, silly me, I was trying to use the line of code in something else by itself. I now know that my COM object is of type “jscripttypeinfo” thanks to your script.

    Can I use your class to extract the integer array that is inside it? If so, how would I go about it.

    The integer array is from a classic asp page, so in reality it is an array of variants, but I don’t know how to access it in C#. Any help would be great.

  • Andrei Smolin (Add-in Express Team) says:

    If you cannot cast the object to the appropriate type (jscripttypeinfo), then you need to use late binding. To find out what that object makes available for you, use the technics desribed in Inspecting COM Objects With Reflection, see https://msdn.microsoft.com/en-us/magazine/dd347981.aspx.

  • Graham says:

    Great, thanks. Just need to figure out what the VB code is doing and convert it into C# I guess – wish me luck :-)

  • Andrei Smolin (Add-in Express Team) says:

    Wish you luck )

    A free CSharp –> VB and VB to Csharp convertor – https://www.developerfusion.com/tools/convert/vb-to-csharp/.

  • Graham says:

    OK, the first bit of VB converted to:

    foreach (object m in arr.GetType().GetMembers())
    Console.WriteLine(m);

    The output I get is:
    System.Object GetLifetimeService()
    System.Object InitializeLifetimeService()
    System.Runtime.Remoting.ObjRef CreateObjRef(System.Type)
    System.String ToString()
    Boolean Equals(System.Object)
    Int32 GetHashCode()
    System.Type GetType()

    Doesn’t mean anything to me yet.

    Thanks for the converter link – will give it a try.

  • Robinson says:

    Hey, this is great. Just what I needed.

    Thanks very much Andrei.

  • jozsef says:

    I have been looking for this solution for days. Thank you so much.
    There is still another question that I haven’t found a solution for.

    If a local method has a com object as a parameter, GetMethod returns a null. It shows up in the return of GetMethods and sequence through them; but that is so not elegant. Do you have any suggestions?

  • Andrei Smolin (Add-in Express Team) says:

    Hello Jozef,

    In my practice, this sort of problem occurs if you set the bindingAttr parameter of the GetMethod method incorrectly. The code below works fine for me.

    private void adxRibbonButton1_OnClick(object sender, IRibbonControl control, bool pressed) {
    System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
    System.Type addinModule = asm.GetType(“MyAddin23.AddinModule”);
    System.Reflection.MethodInfo myMethod = addinModule.GetMethod(“ProcessPresentation”, System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    }
    private void ProcessPresentation(PowerPoint.Presentation pres) {
    //
    }

Post a comment

Have any questions? Ask us right now!