Cottleston Pie

Fernando Felman’s thoughts on software development

HOW TO: Check the Type of a COM Object (System.__ComObject) with Visual C# .NET

Posted by Fernando Felman on February 5, 2008

This one is a favourite of mine since I tried to resolve it in the past with no success.

The problem at hand is that we have a COM object o returned from Excel interop and we have to figure out what underlying interface it implements. If you try to use GetType() you’ll get a __ComObject which does not provide any helpful information as to what underlying types are implemented by the object.

The following KB article sheds some light: http://support.microsoft.com/kb/320523. The suggested way in the article is to manually cast the object to any known type using the as directive and if a valid object is returned – that type is implemented by the object. This solution will work, but it’s utterly ugly and requires vast amounts of typing in order to support all the possible types. I needed to implement something similar without using manual casting, essentially I needed a method that receives a COM object (of the type __ComObject, assuming it was created by the Excel interop) and returns the underlying interface supported by that object.

First I thought to somehow ask the object to provide me with a list of implemented interfaces. Something like the System.Type.GetInterfaces method. The problem is that the type of the object does not expose any useful information and the object itself has no typed class, so that proved to be futile.

Then I thought that instead of getting the implemented types from the object, I can query all the existing types one by one. Sure, it’s not as pretty as the previous path but that’s actually how COM works by design. Every COM class implements the IUknown interface which supports the QueryInterface method used to get a pointer to where an interface is implemented in the class. The idea is that all the information about what methods are supported by the interfaces and what interfaces are implemented by a type should be acquired in advance and coded into the caller (IDL, TLB and h files comes in mind). There was no true run-time reflection in the old COM days.

.NET supports COM by means of interoperability proxies. I won’t get into too many details, but basically you get .NET objects typed as __ComObject mapping COM instances and .NET interfaces with some unique attributes mapping COM interfaces. More to the point, you get the System.Runtime.InteropServices.Marshal class to handle all bunch of COM and interop operations such as calling the QueryInterface method on a .NET object mapped to a COM instance.

So now we know that we can query interop interfaces against the object. Sweet. But in order to do that we need the Interface ID, or iid, which is the GUID identifying an interface in COM. Oh, and we need that for each end every COM interface implemented by the Excel interop. Not sweet. OK, but we’re in .NET world now (thanks to the System.Runtime.InteropServices namespace) which has great support for runtime reflection. How difficult would it be to enumerate all the COM interfaces implemented by the Excel interop? Not too difficult really, we can use the System.Reflection.Assembly class to get a handler to the Excel interop assembly and from there it’s relatively easy going.

So let’s conclude what we need:

  1. Get all the COM interface types exposed by the Excel interop assembly.
  2. Fetch the Interface ID of each type and use it on QueryInterface to test whether an interface is implemented by the object (if a valid pointer is returned, the interface is implemented by the object).

This doesn’t sound too complicated. Obviously there are some syntax and plumbing issues to address, but the concept is rather simple. Good, let’s implement it:

GetExcelTypeForComObject method

Namespaces:
using Excel = Microsoft.Office.Interop.Excel;
using interop = System.Runtime.InteropServices;

Note that in many cases a wrapped COM object implements many interfaces, so breaking after the first implemented interface is found might not do it for you…

Advertisements

20 Responses to “HOW TO: Check the Type of a COM Object (System.__ComObject) with Visual C# .NET”

  1. Excellent.

    I want to be able to access properties of the particular type found. e.g.

    object oSelect = ExcelApp.Selection;
    Type type = GetExcelTypeForComObject(oSelect);

    //say that the type returned is Microsoft.Office.Interop.Excel.Arc
    //Arc has a ShapeRange property
    //how do I do this line??
    Excel.ShapeRange shapeRange =(oSelect as type).ShapeRange

    Regards
    Andrew

  2. Hi Andrew,
    if my method found that the underlying type is an Arc, then the following static casting should work:
    Excel.ShapeRange shapeRange = (oSelect as Microsoft.Office.Interop.Excel.Arc).ShapeRange

    On the other hand, if you want to dynamically cast oSelect based on the returned type, you’ll have to use reflection.

    Cheers,
    Fernando

  3. Elmue said

    Hello

    Thanks, your article helped me a lot.
    I still have a problem.
    I’m trying to set properties of a PowePoint Table Cell.

    In C# this looks like:
    Cell.Borders[PpBorderType.ppBorderTop].DashStyle = MsoLineDashStyle.msoLineDash;

    But I want to be able to set ANY property of ANY ComObject 100% dynamically using information from a XML file.
    Is there any why to find out WHICH enumeration type uses the Borders[] collection ?

    It would be great if the was something like

    Type GetEnumerationType(Type i_ComObject)
    {

    }

    so that calling
    GetEnumerationType(typeof(Borders))
    would return
    PpBorderType

    There MUST be any way because Visual Studio complains if I put any invalid enumeration type like:

    Cell.Borders[MsoTextOrientation.msoTextOrientationHorizontal]…

    So this information exists in the typelib.
    But how di I get it ?

    Any idea ?
    Thanks
    Elmü

  4. Elmue said

    Hello

    There is something missing in your code:

    Marshal.Release(ipointer);
    ..
    Marshal.Release(iunkwn);

  5. Elmue said

    Hello

    I found out how to do it!

    Your solution with a loop searching among all interfaces of a namespace
    and cheking the result of QueryInterface() is extremely slow.

    I found a much better solution which is as fast as normal reflection and also 100% dynamic.

    If you came here because you are searching for a way to use Reflection on COM objects,
    download my OpenSource project PowerPointCreator version 3.0.

    http://www.codeproject.com/KB/office/PowerPointCreator.aspx?msg=2708475#xx2708475xx

    There you see how to do it!
    I also solved the problem I asked yesterday here.

    Elmü

  6. Elmue,
    I’m happy you found a solution to your question however I couldn’t find the “much better solution which is as fast as normal reflection” to get the underlying COM Interfaces implemented by any .NET interop object. By reviewing your source code I could only see XML manipulation but no COM or .NET interop manipulations. Could you please post here a short snippet of how to do so without the usage of the “extremly slow” loop I’m using in this article?

    Cheers,
    Fernando

  7. Michael said

    I gave up and wrote the code in VB.NET as a class library since you don’t need to worry about reflection in VB.NET and then used that in my C# project.

  8. Michael said

    My suggestion of using VB.NET works for about half the properties that were being reflected as __ComObject but unfortunately there are a number that don’t work so finished up using your get type method. Thanks for posting it.

  9. Bjørn Egil Hansen said

    Hi Fernando,

    Thanks for useful information pointing me in the right direction.

    In my case, I was looking at how to make a generic .NET utility for copying a specified set of properties between two objects in SAP Business One DI API (COM library wrapped with COM Interop). Using GetType directly, I got the same problems as described above – just System.__ComObject with no useful information (for this purpose). Looking up the desired type in the assembly, I get all the meta-information, including the PropertyInfo’s for getting and setting property values.

    Rather than iterating all types in the assembly, I lookup the desired type directly and use this for manipulating the object. See code example below:


    SAPbobsCOM.Items item =
    (SAPbobsCOM.Items)sapCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.oItems);
    System.Reflection.Assembly sapAssembly =
    System.Reflection.Assembly.GetAssembly(typeof (SAPbobsCOM.CompanyClass));
    Type itemType = sapAssembly.GetType("SAPbobsCOM.IItems");
    PropertyInfo propInfo = itemType.GetProperty("ItemCode");
    object value = propInfo.GetValue(item, null);

    Cheers,
    Bjørn Egil

    • Satyajit said

      Thank you SO MUCH Bjørn! What you’re doing is exactly what I needed!

      I can’t believe I spent 6 hours on stackoverflow.com and codeproject.com and msdn.microsoft.com, with a such an simple solution already available.

  10. Alan Churchill said

    This saved my bacon after I thought I was at a wall.

    Thanks much.

    Alan

  11. Neat slice of code. Thanks!

  12. reza said

    Fernando’s solution to get the type of the interface is cool however I have come to believe that if the public members of a COM object are not exposed through an interface, there is no way with CSharp to call those members whereas it seems VB does not mandate such a thing.

    In the past I wanted to access Outlook objects and finally I realised that to access a member of an outlook object, I had to get the member from the interface of that object and invoke it on the instance of the object. In VB.NET I would not have to as I could call a late-bound method on an instance of a COM object and it would find out what to call.

    Currently, I am trying to call some methods of a COM object which does not have its public members exposed via an interface. I have not been able to figure this problem out in CSharp so I am going to use VB.NET in a library to do this bit for me. I cannot believe this is true, I always thought CSharp and VB.NET have the same features, or if there is a difference, CSharp can do something VB.NET cannot.

    • Hi Reza,
      I can’t comment on how VB implements late binding of COM objects, so I can’t really say what’s the different between VB and C#; however, I think that if there’s a different, that different would be in the services the IDE gives you rather than the actual capabilities of the language. At the end of the day, both languages complies to the same CLR…

      Anyway, there have been quite a few changes in the next version of .NET, targeting some very interesting COM-interop scenarios. You may find this post interesting (although a bit old now, i mentioned that post in here). The included document talks about some topics such as dynamic lookup & the dynamic type, named & optional arguments and some other COM-related goodies. These features are likely to make it much easier to use COM in C#.

      • reza said

        Correction to my earlier post, the sample VB code I had was VB 6.0 not VB.NET. I am a CSharp dev and have not even installed VB.NET on my machine otherwise I would double check before I put that post up. Sorry for the confusion I caused.

        I tried the VB code in a VBA IDE (Excel) and found it able to call public members of a COM object that I cannot see or call with CSharp. In VB 6.0 or VBA (the one on Microsoft Office) methods of a COM object can be called like obj.Method() as if they are members of the object whereas they will be verified at run-time. Apparently VB 6.0 takes a deeper look into COM objects that .NET reflection does not, or I do not know how to get .NET reflection to do it. I am sure it is just a tweak and I am desparately looking for it.

      • Right, so we are talking COM vs. .NET here… Very different beasts.

        Look, I won’t go into a detailed explanation of how COM and VB6 works, but let me assure you it’s extremely different than how C# / .NET works. In a nutshell, VB6 uses interface declaration (in TLD or IDL) and the IUnknown COM interface to invoke method calls by their vtable address. On top of that, both VB6 and VBA uses IDispatch to invoke methods by their names (rather than the vtable address of the method). BTW, BV6 is not VBA – they both use COM under the hood, but they uses very different approaches and interfaces to actually compile & execute code.

        So, if we focus on VB6 only, the CLR can do everything that VB6 can do, but VB6 gives you better user experience than say C# (e.g. better intellisense). The main reason is that VB6 creates a new instance of every object you declare and this instance is aware of the interface declaration. In C#, you relay on COM-interop objects that do not expose this information and so you’ve to do some “dirty” tricks as I did in this post to get hold of the “real” interface you’re after which then allows you execute the method you’re after.

        Example: suppose you’ve a COM object that implements 2 interfaces, each exposes 1 method. Let’s call them “interface1.method1” and “interface2.method2”.

        In VB6, you’ll write something along the lines of “dim myObj = new MyClass”. At that point, VB6 knows what “MyClass” means because it exists in the references of the VB6 project. This means that VB6 knows what interfaces MyClass implements, so when the intellisense pops up, you’ll see that the object implements 2 methods: “method1” and “method2” (note that the actual interfaces from which the methods are exposed, are now gone). From that point, it’s really easy to write stuff like “myObj.method1” and VB6 will know that you mean “please invoke method1 from interface1 on the instance of an object of the class MyClass”.

        Now, back to .NET world. In .NET, the COM interfaces “interface1” and “interface2” will exist in the interop assembly. However, when you get a COM-marshalled object its .NET interface would be of the type “__ComObject” which obviously know nothing about “interface1”, “interface2” or their methods (“method1”, “method2”). So, in order to invoke “method1” you must cast the object to “interface1”. Once you do the casting, you’ll be able to invoke all the members of the interface (and you can use reflection to invoke non-public members, if that’s indeed the case).

        What I do in this post is to display an approach to identify what interfaces a COM-interop object implements, using brute-force. You pass the algorithm a com-object and some interfaces (originated from an assembly), and the algorithm will identify what interfaces the com object implements. using this information, you can use the object to invoke any member from any of the implemented interfaces.

        Hope this helps 🙂

        PS, VBA is yet a different beast altogether and I really don’t want to go into it. basically, it uses a slightly different approach to invoke COM method in objects that implements some specific automation-related interfaces.

      • Reza said

        Thanks Fernando, it was really appreciated you shared all this with me, I enjoyed reading it.

        I do understand every point you raised regarding how different platforms deal with COM objects and I think we have narrowed down the path to the problem I am facing right now. My problem is although the COM object I am using has several interfaces and I can see the interfaces using your code – thanks for that! – none of the interfaces expose the methods I like to invoke on the COM object. Actually, when I added the COM object to the references of my VS solution I found out that the COM object implements an interface that cannot be seen with your code. I tried to get the type of the interface from the assembly instance of the COM object type but it returned null. How some interfaces in such an assembly cannot be accessed? What if they cannot be accessed at all? If I cannot get to the type of that interface, I will not be able to invoke any method of the COM object.

        Please note that, because there are different versions of the COM object around (similar to the case of Ms Outlook), I have to load the types from registry so referencing is not an option however it can be used for insighting into the objects.

      • Hey Reza,
        Glad to hear you find it useful, it’s been quite a while since I last touched any code and this brings some good memories back… 🙂

        Anyway, back to your issue: you’ve a COM-marshalled object and you’ve a COM interface with a method you’d like to invoke. Problem is, you can’t find the interface you’re after in the .NET domain and so you can’t invoke your desired method.

        With this in mind, we can articulate what we want to accomplish: we want to get a .NET handle (of the type System.Type) to the COM interface you know the COM-marshalled object implements.

        I can think of some few circumstances that may cause the problem. One possible cause could be that the Office interop assemblies don’t map the desired interface. Another possible cause is that the interface is mapped, but in an assembly that we’re not expecting it to be.

        Let’s start with cause #2: interface exists in a .NET but we don’t really know where. The easiest way to find if that’s indeed the case is to use a tool such as reflector.NET to inspect the office interop assemblies and identify if your interface lives in any of the assemblies.

        If you couldn’t find your interface, then you’ve to create an interop yourself. In this case you’ve to do a dual-phase job: first you’ve to extract the interface from the registry into something like IDL or TLB and then you have to code the .NET interop based on the extracted interface declarion (you could probably code-generate the interop rather than manually code it).

        I’m not sure how to extract a TLB or IDL from the registry, but presumably if you search the net you’ll find some tools that can do that. Saying that, I think you could just use the “real” TLB / IDL – the same one that VB6 uses – but you have to find the correct file.

        Once you’ve an interface declaration, you should follow the instructions in these posts to created the .NET interop proxy. From that point, it’s relatively easy to cast the object to your interface and invoke the method.

        Good luck! 🙂

  13. Mike Bridge said

    Thanks—that saved me a huge amount of time dealing with reflection on a COM object with 300 or so types.

  14. chat said

    Thanks it helped me a lot in working with MS Word.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: