Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I want to invoke a method on a COM object that is defined in .NET from another .NET application using the DispID.

I have the following class and interface defined in a .NET project:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace TestComObject
{
   [ComVisible(true)]
   [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
   [Guid("99476c04-574a-4271-abc8-f1d2615c3d93")]
   public interface IKnowEverything
   {
      [DispId(1030)]
      string GetTheTruth();
   }

   [ComVisible(true)] 
   [Guid("9B869BAF-622A-458B-BF80-2CD6D8A3C541")]
   [ClassInterface(ClassInterfaceType.None)]   
   public class AllKnowing : IKnowEverything
   {
      public string GetTheTruth()
      {
         return "I am King!";
      }
   }
}

I build it and it is set to register for COM interop, it is signed with a strong key.

Now I want to invoke this method using its DispID from another .NET application. I wrote the following test method:

static void Main(string[] args)
{
   Guid clsid = new Guid("9B869BAF-622A-458B-BF80-2CD6D8A3C541");
   Type type = Type.GetTypeFromCLSID(clsid, true);
   object instance = Activator.CreateInstance(type);

   string methodName = "GetTheTruth";
   string methodDispIdName = "[DispID=1030]";

   string s1 = (string)type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, instance, null);
   Console.WriteLine("Via name: {0}", s1);

   string s2 = (string)type.InvokeMember(methodDispIdName, BindingFlags.InvokeMethod, null, instance, null);
   Console.WriteLine("Via DispID: {0}", s2);
}

The first invocation is successful and the application prints "Via name: I am King!".

But on the second invocation (using the DispID) I get an exception:

Unhandled Exception: System.MissingMethodException: Method 'TestComObject.AllKnowing.[DispID=1030]' not found.

The type library looks okay to me when looking at it in the OLE-COM Object Viewer:

// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: TestComObject.tlb

[
  uuid(139DAA99-BF76-4057-BCC8-DCB35C153920),
  version(1.0),
  custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, "TestComObject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58b6bd22fbd98427")

]
library TestComObject
{
    // TLib :     // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}
    importlib("mscorlib.tlb");
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    dispinterface IKnowEverything;

    [
      uuid(99476C04-574A-4271-ABC8-F1D2615C3D93),
      version(1.0),
      custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "TestComObject.IKnowEverything")    

    ]
    dispinterface IKnowEverything {
        properties:
        methods:
            [id(0x00000406)]
            BSTR GetTheTruth();
    };

    [
      uuid(9B869BAF-622A-458B-BF80-2CD6D8A3C541),
      version(1.0),
      custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "TestComObject.AllKnowing")
    ]
    coclass AllKnowing {
        interface _Object;
        [default] dispinterface IKnowEverything;
    };
};

What am I doing wrong here?

share|improve this question
From Hans Passant's answer here: social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/…, when using "[DispID=1030]" as the name of the member, you should call InvokeMember on the type instance retrieved from Type.GetTypeFromProgID(), not Type.GetTypeFromCLSID – phoog Feb 14 '12 at 20:47
The Type returned ought to be the same whether I get it by ProgID or CLSID, shouldn't it? – DeCaf Feb 15 '12 at 7:58
I would think so, but the documentation for GetTypeFromProgID, seems to say that the return value is always System.__ComObject, while the documentation for the other method doesn't say this. Of course, this wouldn't be the first instance of inconsistency or error in the framework documentation. Since Hans gave a different answer to your question, I guess you are right about the returned Type. – phoog Feb 15 '12 at 15:20
Interesting, I will try it and see if there is a difference, and if not file a bug report about the MSDN documentation then I guess ;) – DeCaf Feb 15 '12 at 16:27

1 Answer

up vote 2 down vote accepted

This doesn't work because you implemented the COM server in .NET. The instance member is an actual .NET object, not an RCW. The can tell from the debugger, it is not a System.__ComObject but an object of the actual .NET type.

So when you use InvokeMember(), you get the System.Reflection version of it, not the IDispatch version. Another way to see it is to use "getTheTruth". Note how the first attempt fails now as well. IDispatch is case-insensitive, System.Reflection is not. The "[DispID=1030]" trick doesn't work because System.Reflection doesn't know anything about disp-ids.

Feature, not a bug, it doesn't make sense for the CLR to create the RCW when it can create the .NET object directly. Nor would that be easy to implement.

share|improve this answer
Was not aware of this, thanks for the information. It makes sense in some ways I guess. – DeCaf Feb 15 '12 at 7:58

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.