none
Two questions about "COM" RRS feed

  • Question

  • Hello all:

    Just a newbie to COM (Component Object Model) because I began to learn programming based on .NET;) So I summarized several things please confirm "Yes" or "No" with some reasons;)

    My C# codes:

    namespaceCOMTest
    {
        publicinterfaceIMyClass
        {
            int Fun();
            int Fun2();
        }
     
    [ClassInterface(ClassInterfaceType.None)]     public class MyClass     {         public int Fun()         {             return 3;         }         public int Fun2()         {             return 3;         }     }

    Q1:is I cannot understand if I DON'T define the interface and set "Make Assembly COM Visible" = True, I cannot run as:

    Dim a As New MyClass
    a.Fun()

    But I can run with CreateObject method in VB6. Why?

    Q2:Without defination of:

    [ClassInterface(ClassInterfaceType.None)]

    I can write:

    Dim a As New MyClass
    a.Fun()

    And run well, but when I say "a." WITHOUT ANY INTELLISENSE OF FUN", why?

    Thursday, January 23, 2014 8:05 AM

Answers

  • COM history lesson: There are 2 core COM interfaces: IUnknown and IDispatch.  IUnknown is designed for early-bound clients such as C++.  The compiler must be able to get the COM interface details at compile time so it can validate the calls.  This is the fastest way to call a COM object as the compiler does the leg work.  It is really not much different than a normal type definition.  To get the interface you would run the IDL (the interface definition) through midl which generates the appropriate compile-time source files you need.

    IDispatch is designed for late-bound clients such as VB (prior to .NET) and scripting languages.  Late bound clients don't bother validating the interface contents until runtime.  This means that the client can actually refer to things that don't exist yet, provided they exist by runtime.  IDispatch contains a set of methods that allow a client to query for the members that are supported along with their signature and then to make the actual call.  The runtime overhead is higher but the decoupling is better.  VB doesn't understand IUnknown and therefore will not work with COM components that don't expose IDispatch.

    Back to the show: In your very first example MyClass set CIT to None and therefore it did not have any members because you also didn't specify an interface.  If you generate a TLB for the assembly you'll find that the class only implements the _Object interface (which are the standard Object members).  Your IMyClass interface is also exposed to COM but your class doesn't implement that.

    CreateObject works by instantiating a COM object and then using IDispatch.  This approach will likely not allow you any sort of Intellisense because the compiler has no way of knowing what object is going to be returned (you're just passing a CLSID).  Hence don't expect IS to work at all.  This is true late binding.  If you try to new up the object you're actually using the a hybrid approach where the compiler is going to use the typelib but I don't guarantee IS works.

    If you remove the CIT reference then you're telling .NET to figure it out for you so it'll define an interface to back your class (_MyClass) and that interface will support IDispatch.  Since it supports IDispatch you can call it from VB.  But since it is late bound I doubt Intellisense will work.

    The final case (which you didn't provide) is when you use CIT.None but you provide the interface. In this case your .NET interface is exposed directly as the COM object (rather than _type).  With no other indicators on the interface it'll be an IDispatch interface. 

    As an aside you also have the InterfaceType attribute that is applied to COM interfaces and indicates whether the interface should be exposed as IUnknown or IDispatch.  The default is both.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    • Marked as answer by ThankfulHeart Thursday, January 30, 2014 4:48 AM
    Friday, January 24, 2014 3:30 PM
    Moderator

All replies

  • If you mark a class (or worse yet an entire assembly) as COM visible but you do not define an interface for the type then one will be auto generated for you.  This is bad for many reasons:

    • COM is GUID based and you have no control over the GUIDs.  Doing a clean rebuild will cause new GUIDs to be generated each time which will cause havoc.
    • COM objects generally are registered (unless you're using XP's registration-free components) post build.  If the GUID changes then the old COM object isn't unregistered before the new one is causing you to have lots of bad COM objects registered.
    • If you build the COM object, create a test client (which will use the current COM object GUIDs) and then do a clean rebuild your test client will still be using the old COM object interface so you run into all sorts of issues

    This applies to both classes and the interface defined for the class.  Furthermore the interface may contain things you don't want exported (ToString(), GetHashCode(), etc). 

    The general guidelines for creating .NET-based COM objects is:

    • Never mark an entire assembly as COM visible unless it is purely a PIA (rare)
    • Mark only the classes you want to be visible as ComVisible
    • Add a GuidAttribute to each class to be exposed
    • Define a COM-visible interface that each class will implement
    • Add the GuidAttribute to the interface(s) that are exposed
    • Ensure the assembly has a GuidAttribute
    • Add ClassInterfaceTypeAttribute set to None for each class

    When you specify ClassInterfaceType of None then no COM interface is defined for the type other than what interfaces you specify.  VB traditionally uses IDispatch (which is the default if you don't specify CIT).  IDispatch dynamically determines the members by querying the interface.  Here's a link to more info on how to expose COM types in .NET.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    Thursday, January 23, 2014 5:18 PM
    Moderator
  • Many thanks CoolDadTx:

    But why must I define an interface? And if I don't define, I cannot directly use the initialized instance?

    Another question is why I must refer TLB instead of DLL in VB6? If I refer DLL, an error will be reported.


    ASP.NET Questions
    Other Discussions
    FreeRice Donate
    Issues to report
    Free Tech Books Search

    Friday, January 24, 2014 1:14 AM
  • COM history lesson: There are 2 core COM interfaces: IUnknown and IDispatch.  IUnknown is designed for early-bound clients such as C++.  The compiler must be able to get the COM interface details at compile time so it can validate the calls.  This is the fastest way to call a COM object as the compiler does the leg work.  It is really not much different than a normal type definition.  To get the interface you would run the IDL (the interface definition) through midl which generates the appropriate compile-time source files you need.

    IDispatch is designed for late-bound clients such as VB (prior to .NET) and scripting languages.  Late bound clients don't bother validating the interface contents until runtime.  This means that the client can actually refer to things that don't exist yet, provided they exist by runtime.  IDispatch contains a set of methods that allow a client to query for the members that are supported along with their signature and then to make the actual call.  The runtime overhead is higher but the decoupling is better.  VB doesn't understand IUnknown and therefore will not work with COM components that don't expose IDispatch.

    Back to the show: In your very first example MyClass set CIT to None and therefore it did not have any members because you also didn't specify an interface.  If you generate a TLB for the assembly you'll find that the class only implements the _Object interface (which are the standard Object members).  Your IMyClass interface is also exposed to COM but your class doesn't implement that.

    CreateObject works by instantiating a COM object and then using IDispatch.  This approach will likely not allow you any sort of Intellisense because the compiler has no way of knowing what object is going to be returned (you're just passing a CLSID).  Hence don't expect IS to work at all.  This is true late binding.  If you try to new up the object you're actually using the a hybrid approach where the compiler is going to use the typelib but I don't guarantee IS works.

    If you remove the CIT reference then you're telling .NET to figure it out for you so it'll define an interface to back your class (_MyClass) and that interface will support IDispatch.  Since it supports IDispatch you can call it from VB.  But since it is late bound I doubt Intellisense will work.

    The final case (which you didn't provide) is when you use CIT.None but you provide the interface. In this case your .NET interface is exposed directly as the COM object (rather than _type).  With no other indicators on the interface it'll be an IDispatch interface. 

    As an aside you also have the InterfaceType attribute that is applied to COM interfaces and indicates whether the interface should be exposed as IUnknown or IDispatch.  The default is both.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    • Marked as answer by ThankfulHeart Thursday, January 30, 2014 4:48 AM
    Friday, January 24, 2014 3:30 PM
    Moderator