16

Say I have a class BaseClass that implements IBaseClass

Then I have an interface IClass that inherits IBaseClass.

Then I have a class named class that implements IClass.

For example:

[ComVisible(true), InterfaceType(ComInterfaceType.IsDual), Guid("XXXXXXX")]
public interface IBaseClass
{
  [PreserveSig]
  string GetA()
}

[ComVisible(true), InterfaceType(ComInterfaceType.IsDual), Guid("XXXXXXX")]  
public interface IClass : IBaseClass
{
  [PreserveSig]
  string GetB()
}

[ComVisible(true), ClassInterface(ClassInterfaceType.None), Guid("XXXXXXX")]
public class BaseClass : IBaseClass
{
  public string GetA() { return "A"; }
}

[ComVisible(true), ClassInterface(ClassInterfaceType.None), Guid("XXXXXXX")]
public class Class : BaseClass, IClass
{
  public string GetB() { return "B"; }
}

When exposing to COM, if I make an instance of "Class" it does not allow me to call GetA().

When looking my IDL in the .tlb file, my IClass interface looks like:

[
  odl,
  uuid(XXXXXXXXXXXXXXXXXXXXX),
  version(1.0),
  dual,
  oleautomation,

]
interface IClass : IDispatch {
    [id(0x60020000)]
    BSTR GetB();
}

It doesn't even look like IClass derives from IBaseClass!

If I take out where IClass derives from IBaseClass and just add the methods to the interface, it works.

How can I make C# enable this inheritance in COM? I'd rather not re-implement interfaces when I can inherit them.

CRAP: check this link .Net COM Limitation

If someone has an answer to why this is, or a better workaround than copy-paste to my "derived" interface, let me know. If not, I'll mark an answer in a couple days.

3
  • You need to query for the other interfaces to access methods in them. Depending on the language you use the object from, it might be inconvenient. To work around it you either need to create a "flat" interface for each class or let the compiler do it for you with the "ClassInterface" (which have other problems) Commented Dec 7, 2009 at 22:17
  • 1
    If you see my link, the answerer mentions that .Net does not apply base interfaces to COM interfaces when exported. So in other words, you have to copy-paste the base interface definition to simulate a base interface. Commented Dec 7, 2009 at 22:33
  • 1
    I think you should post your own .Net COM Limitation link as an answer - it's the correct solution, and it's recommended to post your own answer if you figured it out yourself. Commented Sep 22, 2010 at 21:05

4 Answers 4

15

This is not a .NET problem, it is a consequence of the way COM works. It doesn't support inheritance. You would fix this at the client side, it needs to call QueryInterface() with the IID for IBaseClass to get an interface pointer to the IBaseClass interface so it can call GetA(). .NET interop automatically provides a QI implementation that makes this work. However, it is not very user-friendly in this case, design your C# side code to make it easy for the client to use your class instead of the other way around. You'd typically need a one-liner override method that delegates to the base C# class implementation.

Note that there's a problem with your method signatures and the use of the [PreserveSig] attribute. They are not callable through IDispatch nor can they be auto-marshaled. That requires a method with a HRESULT as the return value type. It is automatic when you remove the attribute.

Sign up to request clarification or add additional context in comments.

6 Comments

I originally set [PreserveSig] for ease of use with my testing in VBScript (who wants to mess with out parameters), but it works the same without (and has proper IDL definition). I am going to replicate methods in the base class to prevent the client from having to call QueryInterface.
IMHO this is a .net problem, COM totally supports interface (not class) inheritance, and regasm could have generated a TLB that had IClass deriving from the ComVisible IBaseClass. I note that when this happens, the IDispatch implementation of Class is not aware of IBaseClass methods either - very disappointing. Your advice (QI) is good in the case where one coclass implements multiple interfaces; according to the MVP in Jonathan's link, that technique won't work with an inheritance hierarchy because it's simply not supported.
@bacar: aggregation is arguably the better term. Anyhoo, it takes a concrete class to actually implement the interfaces. And to make inherited COM interfaces work, the compiler needs to generate multiple v-tables, one for each interface. That's only supported by runtime environments that support multiple inheritance. .NET is not one of them, a pretty fundamental limitation. Yes, you could call that a .NET problem, it isn't going to get you anywhere.
@Hans: surely only 1 (deep) vtable is needed, corresponding to the most derived interface? Multiple vtables should only be needed when there is multiple inheritance, which is not what the OP is after. (and even then, .net supports inheritance/aggregation from multiple interfaces anyway).
@bacar - a deep v-table is troublesome. It needs to do multiple incompatible duties, support the interface method pointers as well as the virtual methods of the class itself. You cannot control the placement of the pointers. And a .NET class always has virtual methods, it inherits System.Object. I don't disagree that the CLR could have done a better job synthesizing a set of separate v-tables especially for COM clients. But it didn't happen, they targeted Automation. Hacking IUnknown interfaces is possible, just not pretty. There's nothing pretty about not having a type library anyway.
|
6

This appears to be very similar to this question: Interface inheritance in ComVisible classes in C#

As you noted in your edit, this is a limitation on the .net to COM hop, that the COM interface does not include any inherited interfaces.

In some languages you could have solved this by inculding a block of code in all relevant interfaces, but as far as I am able to see this is not possible in C#.

2 Comments

It turns out that this is just going to be some annoying extra work to make our assembly com-visible.
With C# 8.0 traits programming, it is now possible to include a block of code in interfaces!
0

Here is a different Approach that minimizes the work, and leaves the interface clean.
Just provide an additional Property which returns the BaseInterface:

public IBaseClass BaseProperties {
    get { return this; }
}

Of course you have to add thise part in your IBaseClass Interface:

public ISingleShot BaseProperties { get; }



Here is the corresponding VB.Net Code:

ReadOnly Property BaseProperties As ISingleShot
Public ReadOnly Property BaseProperties As IBaseClass Implements IClass.BaseProperties
  Get
    Return Me
  End Get
End Property

Comments

0

use [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] as an attribute for the interface that extends the others. it is a workaround.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.