locked
Exposing the full interface of a class to COM RRS feed

  • Question

  • Hi,

    I have a base class with an explicit interface (*01), and a child class with its own interface (*02), and I'm trying to have all the child's full interface (so, the contents of IInterface01 AND IInterface02) exposed to COM, but haven't found a way to do it. Here is the code :

        [ComVisible(true)]
        [Guid("E0D744B2-85AB-4ab8-A964-E462F3839BC4")]
        public interface IInterface01
        {
            int Test01(int a, int b);
    
        }
    
    
        [ComDefaultInterface(typeof(IInterface01))]
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.None)]
        [Guid("728D6035-B927-4f96-ACD8-67DD04EAF746")]
        public class TestClass01 : IInterface01
        {
            public int Test01(int a, int b)
            {
                return a + b;
            }
        }
    
    	
        [ComVisible(true)]
        [Guid("9E88A426-1DF9-4357-9CAD-B98FBDC23C10")]
        public interface IInterface02 : IInterface01
        {
            string Test02(string s);
    
        }
    
    
        [ComDefaultInterface(typeof(IInterface02))]
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.None)]
        [Guid("212E83ED-8538-4cb3-BD77-40E12375CEAE")]
        public class TestClass02 : TestClass01, IInterface02
        {
            public string Test02(string s)
            {
                return s + "abcdef";
            }
        }
    


    If I create an instance of TestClass02 in VB6 :

    Dim t02 As TestClass02
    Set t02 = New TestClass02
    t02. ' <--

    I only have access to the method Test02, not Test01.

    I've tried with and without IInterface02 inheriting from IInterface01, as well as with and without [ComDefaultInterface(...)].


    Is what I'm trying to do possible (without having to explicitely redefine IInterface01's methods in IInterface02 of course) ? Does it go against the purpose of interfaces ?

    Thanks for your time,
    Gilles

    Thursday, April 23, 2009 1:20 PM

Answers

  • Either mark the class as

    System.Runtime.InteropServices.ClassInterface(Runtime.InteropServices.ClassInterfaceType.AutoDispatch)
        (for late-binding only)
    OR

    System.Runtime.InteropServices.ClassInterface(Runtime.InteropServices.ClassInterfaceType.AutoDual)
        (for both early and late binding)

    rather than
    System.Runtime.InteropServices.ClassInterface(Runtime.InteropServices.ClassInterfaceType.None)


    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.classinterfaceattribute.aspx
    Collin Sauve
    • Proposed as answer by Collin Sauve Thursday, April 23, 2009 2:51 PM
    • Marked as answer by Gilles Korngut Monday, April 27, 2009 7:00 AM
    Thursday, April 23, 2009 2:51 PM
  • Here is the code......

        [ComVisible(true)]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IInterface01
        {
            int Test01(int a, int b);
        }
    
    
    
        [ComVisible(true)]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IInterface02
        {
            int Test01(int a, int b);
            string Test02(string s);
        }
    
    
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.None)]
        [ComDefaultInterface(typeof(IInterface01))]
        [Guid("728D6035-B927-4f96-ACD8-67DD04EAF746")]
        public class TestClass01 : IInterface01
        {
            public int Test01(int a, int b)
            {
                return a + b;
            }
        }
    
    
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.None)]
        [ComDefaultInterface(typeof(IInterface02))]
        [Guid("212E83ED-8538-4cb3-BD77-40E12375CEAE")]
        public class TestClass02 : TestClass01, IInterface02
        {
            public string Test02(string s)
            {
                return s + "abcdef";
            }
        }
    
    

    www.dsmyth.net | www.dsmyth.net/wiki
    Thursday, April 23, 2009 6:13 PM
  • Colin,
    Thanks for your reply. Yes, it works that way, but it completely bypasses interfaces, and also it exposes every public method of the class, some of which you might not want exposed.

    Derek,
    Thanks for the code.


    To sum up, it is not possible to expose to COM all the methods of an inheriting interface without redeclaring the inherited methods.
    The other approach, which is to use ClassInterfaceType.AutoDispatch or .AutoDual and not define any explicit interface, does expose all the public methods (including the inherited ones), but does not let the user choose which methods to expose.

    Thanks all,
    Gilles
    Monday, April 27, 2009 6:55 AM

All replies

  • Hi,

    It's been a while since I have done .NET to COM but I know this problem. At the moment I think the default the COM interface for the interface is being auto generated, AutoDual, and it's usually always better to take control, None.

    ClassInterfaceType Enumeration

      AutoDual     Indicates that a dual class interface is automatically generated for the class and exposed to COM. Type information is produced for the class interface and published in the type library. Using AutoDual is strongly discouraged because of the versioning limitations described in ClassInterfaceAttribute.

    That is what is happening now and the autogenerate process ignores the interface.

    None       Indicates that no class interface is generated for the class. If no interfaces are implemented explicitly, the class can only provide late bound access through the IDispatch interface. This is the recommended setting for ClassInterfaceAttribute. Using ClassInterfaceType.None is the only way to expose functionality through interfaces implemented explicitly by the class.


    You then need to add an attribute to the interface that indicates it will be part of COM. The InterfaceTypeAttribute, this attribute then determines how the interface is exposed to com..

    Listen I hope that was enough information to get you started on the right path. I have done this before and have a code example (somewhere); if you the above is not enough information then post again and I will try and find the source.


    www.dsmyth.net | www.dsmyth.net/wiki
    Thursday, April 23, 2009 2:38 PM
  • Either mark the class as

    System.Runtime.InteropServices.ClassInterface(Runtime.InteropServices.ClassInterfaceType.AutoDispatch)
        (for late-binding only)
    OR

    System.Runtime.InteropServices.ClassInterface(Runtime.InteropServices.ClassInterfaceType.AutoDual)
        (for both early and late binding)

    rather than
    System.Runtime.InteropServices.ClassInterface(Runtime.InteropServices.ClassInterfaceType.None)


    http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.classinterfaceattribute.aspx
    Collin Sauve
    • Proposed as answer by Collin Sauve Thursday, April 23, 2009 2:51 PM
    • Marked as answer by Gilles Korngut Monday, April 27, 2009 7:00 AM
    Thursday, April 23, 2009 2:51 PM
  • Derek,

    Thanks for your reply.

    Unless I'm mistaken, I think I'm already doing what you're suggesting : in the code above, the two classes have their ClassInterfaceAttribute set to ClassInterfaceType.None, while the two interfaces have their InterfaceTypeAttribute set to ComInterfaceType.InterfaceIsDual (implicitly -- it is the attribute's default value).
    As you've said, it is the recommended setting for COM exposure.

    I've played with these parameters but the only two ways I found of having both Test01 and Test02 exposed were :
    - no explicit interfaces and [ClassInterface(ClassInterfaceType.AutoDual)]  for both classes
    - redeclaring Test01 in IInterface02
    Neither of which is very satisfying.

    If you have a code example where an interface's methods as well as the methods it inherits from other interfaces are exposed, I would gladly have a look at it.

    Thanks for your time,
    Gilles

    Thursday, April 23, 2009 4:24 PM
  • Hi,

    The interfaces though need the InterfaceType attribute. Like this...

        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [ComVisible(true)] [Guid("E0D744B2-85AB-4ab8-A964-E462F3839BC4")] public interface IInterface01 { int Test01(int a, int b); }
    Had a look at the code examples and the example I have does not have inherited types. It uses interfaces however. Used to know all about COM interop with .NET so I am going to recreate your example (if the above doesn't work).

    www.dsmyth.net | www.dsmyth.net/wiki
    Thursday, April 23, 2009 4:44 PM
  • Hi Gilles,

    Used to be a time I knew all about this. Ok so I looked at the code examples I have and they don't quite fit your scenario, sure I have inheritance and interfaces but not inherited interfaces and this I believe is the problem. Forgetting the classes for the moment I tried to get the interfaces exporting correctly and no matter how I tried I couldn't get the inheritance to flatten for IInterface02. This is I believe what is stopping the component from being exposed to COM correctly.

    Unfortunately the only way I could get it all working without any inheritance changes was to use ClassInterfaceType.AutoDual (the option that is not recommended). Did a search also, because it's an annoying problem, and found someone else had asked the same question 3 years ago; no-one answered.


    In terms of getting this to work I'd be more inclined to not have inheritance on your interfaces. Here is the code I have...


    This might actually give you an advantage in versioning the COM component; sure it's not a great .NET practice; but it's maybe more a COM practice.

    I have to call it a night, hope this was helpful.




    www.dsmyth.net | www.dsmyth.net/wiki
    Thursday, April 23, 2009 6:10 PM
  • Here is the code......

        [ComVisible(true)]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IInterface01
        {
            int Test01(int a, int b);
        }
    
    
    
        [ComVisible(true)]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IInterface02
        {
            int Test01(int a, int b);
            string Test02(string s);
        }
    
    
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.None)]
        [ComDefaultInterface(typeof(IInterface01))]
        [Guid("728D6035-B927-4f96-ACD8-67DD04EAF746")]
        public class TestClass01 : IInterface01
        {
            public int Test01(int a, int b)
            {
                return a + b;
            }
        }
    
    
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.None)]
        [ComDefaultInterface(typeof(IInterface02))]
        [Guid("212E83ED-8538-4cb3-BD77-40E12375CEAE")]
        public class TestClass02 : TestClass01, IInterface02
        {
            public string Test02(string s)
            {
                return s + "abcdef";
            }
        }
    
    

    www.dsmyth.net | www.dsmyth.net/wiki
    Thursday, April 23, 2009 6:13 PM
  • Colin,
    Thanks for your reply. Yes, it works that way, but it completely bypasses interfaces, and also it exposes every public method of the class, some of which you might not want exposed.

    Derek,
    Thanks for the code.


    To sum up, it is not possible to expose to COM all the methods of an inheriting interface without redeclaring the inherited methods.
    The other approach, which is to use ClassInterfaceType.AutoDispatch or .AutoDual and not define any explicit interface, does expose all the public methods (including the inherited ones), but does not let the user choose which methods to expose.

    Thanks all,
    Gilles
    Monday, April 27, 2009 6:55 AM