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:
- Cast the object to the IDispatch type.
- Get the ITypeInfo interface via IDispatch.GetTypeInfo().
- 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:
14 Comments
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));
}
Thank you for posting, Matt!
I pointed out that way :)
The blog is for C# purists :)
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?
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.
I can’t get C# 2010 express to recognize IDispatch
What using or reference should I add?
It is desclared in the code. Find the string “interface IDispatch” in your code or on this page.
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.
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.
Great, thanks. Just need to figure out what the VB code is doing and convert it into C# I guess – wish me luck :-)
Wish you luck )
A free CSharp –> VB and VB to Csharp convertor – https://www.developerfusion.com/tools/convert/vb-to-csharp/.
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.
Hey, this is great. Just what I needed.
Thanks very much Andrei.
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?
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) {
//
}