33

I am referencing a COM library in Visual Studio, so it has automatically created the corresponding Interop assembly for me. I would like to do a GetType() on these com objects, but they always return System.__ComObject. Querying them for an interface works though:

bool isOfType = someComeObject is ISomeComObject; //this works

But what I really want is this to return the actual type of the com object:

Type type = someComeObject.GetType(); //returns System.__ComObject :-(

Does anyone know how to do what I want to do?

4 Answers4

63

Add reference to Microsoft.VisualBasic.dll and then:

Microsoft.VisualBasic.Information.TypeName(someCOMObject)

MSDN reference here.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • I just tried this and it works! It does not return the full name though, just the class name, but that's ok for my purposes. I looked at this method in reflector which internally calls 'LegacyTypeNameOfCOMObject' but I can't figure out what it actually does. –  Sep 15 '09 at 21:07
  • I wish there was something that could give me the full name of that com object to avoid clashes. –  Sep 15 '09 at 21:13
  • Thanks. Any idea if this is possible in C#? – rpattabi Nov 10 '09 at 06:51
  • 4
    `Microsoft.VisualBasic.dll` is a .NET assembly which can be referenced and used in every application. – Darin Dimitrov Nov 10 '09 at 07:19
  • 3
    Watch out if you have COM component written in C++/ATL. You might get a different result than you expect. – rpattabi Nov 10 '09 at 11:18
  • [Source code for TypeName](http://referencesource.microsoft.com/#Microsoft.VisualBasic/Information.vb,76613a55884f5a36) – Mike Feb 02 '15 at 16:25
  • As @rpattabi mentions, this doesn't always work. In my case, it just returns "_ComObject". – brinkdinges Dec 02 '21 at 16:52
14

The accepted answer by Darin requires a dependency to Microsoft.VisualBasic.dll. If you don't want to have that you can use this simple helper class:

public static class TypeInformation
{
    public static string GetTypeName(object comObject)
    {
        var dispatch = comObject as IDispatch;

        if (dispatch == null)
        {
            return null;
        }

        var pTypeInfo = dispatch.GetTypeInfo(0, 1033);

        string pBstrName;
        string pBstrDocString;
        int pdwHelpContext;
        string pBstrHelpFile;
        pTypeInfo.GetDocumentation(
            -1, 
            out pBstrName, 
            out pBstrDocString, 
            out pdwHelpContext, 
            out pBstrHelpFile);

        string str = pBstrName;
        if (str[0] == 95)
        {
            // remove leading '_'
            str = str.Substring(1);
        }

        return str;
    }

    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("00020400-0000-0000-C000-000000000046")]
    private interface IDispatch
    {
        int GetTypeInfoCount();

        [return: MarshalAs(UnmanagedType.Interface)]
        ITypeInfo GetTypeInfo(
            [In, MarshalAs(UnmanagedType.U4)] int iTInfo,
            [In, MarshalAs(UnmanagedType.U4)] int lcid);

        void GetIDsOfNames(
            [In] ref Guid riid,
            [In, MarshalAs(UnmanagedType.LPArray)] string[] rgszNames,
            [In, MarshalAs(UnmanagedType.U4)] int cNames,
            [In, MarshalAs(UnmanagedType.U4)] int lcid,
            [Out, MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
    }
}
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
  • 1
    This seems to work too - just need to ensure there is a reference to `System.Runtime.InteropServices.ComTypes` for `ITypeInfo` (Visual Studio IDE auto-suggested `Microsoft.VisualStudio.OLE.Interop.ITypeInfo`, but this is _**not**_ the one you want!) – ErrCode Jan 18 '18 at 01:52
  • Is there any way to use this to get the library of the COM object? I have an object from the Word object model for which `TypeName` returns `Shape`, but I cannot cast it to `Word.Shape` [source in VBA](https://stackoverflow.com/q/66300700/111794). – Zev Spitz Feb 21 '21 at 17:33
2

You've basically figured it out. GetType() on a COM object is going to give you System.__ComObject, and you have to try to cast it to something else to see what the object really is.

catfood
  • 4,267
  • 5
  • 29
  • 55
  • Hm..is there really no other way? Because I can't test for every possible COM object in existing, I just want the type. The reason is that I am using the type of an object as a key in a dictionary... –  Sep 15 '09 at 20:56
  • Darin Dimitrov's solution is great. – catfood Aug 17 '12 at 20:43
-2

I stumbled upon this question a few days ago while I was looking for the full type name of a System.__ComObject object. I ended up getting the type name using Darin's solution and then looping through all classes in all assemblies to test the match:

    typeName = Microsoft.VisualBasic.Information.TypeName(someCOMObject);
    foreach (System.Reflection.Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
    { 
        foreach (Type type in assembly.GetTypes())
        {
            if ((someCOMObject as type)!=null)
                fullTypeName = type.FullName;
        }
    }

Not the fastest and most elegant solution, but it worked.

pwhty
  • 7
  • 1
  • 5
  • 1
    `someCOMObject as type`? Not sure if that would work. – nawfal Nov 26 '13 at 13:11
  • Because it doesn't. – Robert Synoradzki Feb 07 '17 at 18:30
  • @nawfal: why not? It's trying to cast "someCOMObject" to the type "type". If it fails (and only then), the result is null. If it doesn't, then it found the correct type. (I know, it took me a few days to come up with that answer :-P) – pwhty Apr 13 '18 at 18:10
  • @pwhty `type` is not a type/class in your code, it is an instance of a type. `as` keyword works on a *type* – nawfal Apr 14 '18 at 18:59