Why isn't there an AutoUnknown value in the System.Runtime.InteropServices.ClassInterfaceType Enumeration?

StackOverflow https://stackoverflow.com/questions/15481134

Question

The System.Runtime.InteropServices.ClassInterfaceType Enumeration in .NET has an AutoDispatch value and an AutoDual value, but no AutoUnknown value. Why doesn't it, and has anyone already come up with a fairly automated way of accomplishing it, so that I don't have to reinvent the wheel?

For a bit more background, here are the three values in the enum currently and what they do:

  • ClassInterfaceType.None does not create a class interface for you. This is Microsoft's recommended value. With no class interface you have to create your own interface with whatever members you intend, and make your class implement it. You can then use the System.Runtime.InteropServices.ClassInterfaceAttribute attribute on your interface to make it a dispinterface (supporting late bound clients), an unknown interface (supporting early bound clients), or dual (supporting both early and late bound clients).
  • ClassInterfaceType.AutoDispatch creates a class interface derived from IDispatch with no explicit members. With no explicit members it can only support late-bound callers.
  • ClassInterfaceType.AutoDual creates a class interface derived from IDispatch that does have explicit members for all that class's public non-static members as well as all those of its base class and any implemented interfaces. Microsoft strongly discourages using this value "because of the versioning limitations" resulting from the fact that if any of the class's members change, the class interface will change as well. (So callers who don't re-bind after the class has changed could end up calling the wrong methods or passing incorrect parameters.)

So what I'm wondering is why there isn't a value called ClassInterfaceType.AutoUnknown which would create a class interface derived from IUnknown that explicitly declares the members of your class (not the base class or other interfaces it implements).

While we're at it, I would also expect there to be something like ClassInterfaceType.AutoDualDirect that would be similar to AutoDual except instead of exposing all members from the base class and all implemented interfaces it would only expose the class's direct members, since the other members could be retrieved through the other COM interfaces.

So what's the story here, what am I missing? I know Microsoft says it recommends against using AutoDual because of the "versioning challenges," and that those concerns would apply to some degree to AutoUnknown too; but despite that recommendation they still have AutoDual. Why wouldn't they also have AutoUnknown?

A word on those "versioning challenges": the most dangerous aspects of those concerns only apply to late-bound callers. AutoUnknown would generate an IUnknown-derived interface, not an IDispatch-derived interface, so we only have to concern ourselves with versioning issues that early-bound clients could have. And since the IID of any auto-generated class interface will change anytime the class's public non-static members change, an early-bound client would not be able to make a "bad call" to existing members, it would simply fail to QueryInterface() for the class interface. Still a failure, but a manageable one, not a crash or anything. This doesn't even break the COM rule of never allowing an interface to change; it's not changing, it's becoming a new interface (new IID). Indeed, it is practically no different from the IUnknown half of the AutoDual mechanism (minus the derived members). In fact, it suffers fewer "versioning challenges" than AutoDual because AutoDual has all the late-bound challenges as well.

So again I have to ask: why does AutoDual exist if AutoUnknown does not!

To give some actual examples, consider the following C# code:

using System.Runtime.InteropServices;

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class F
{
    public int foo()
    {
        return 5;
    }
}

generates the following idl:

[
  uuid(1F3A7DE1-99A1-37D4-943E-1BF5CFDF7DFA),
  version(1.0),
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "F")
]
coclass F {
    [default] interface _F;
    interface _Object;
};

[
  odl,
  uuid(3D0A1144-1C0B-3877-BE45-AD8318898790),
  hidden,
  dual,
  nonextensible,
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "F")    

]
interface _F : IDispatch {
    [id(00000000), propget,
      custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)]
    HRESULT ToString([out, retval] BSTR* pRetVal);
    [id(0x60020001)]
    HRESULT Equals(
                    [in] VARIANT obj, 
                    [out, retval] VARIANT_BOOL* pRetVal);
    [id(0x60020002)]
    HRESULT GetHashCode([out, retval] long* pRetVal);
    [id(0x60020003)]
    HRESULT GetType([out, retval] _Type** pRetVal);
    [id(0x60020004)]
    HRESULT foo([out, retval] long* pRetVal);
};

That's fine, but I would propose that AutoUnknown would helpfully generate this idl instead:

[
  uuid(1F3A7DE1-99A1-37D4-943E-1BF5CFDF7DFA),
  version(1.0),
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "F")
]
coclass F {
    [default] interface _F;
    interface _Object;
};

[
  odl,
  uuid(BC84F393-DACC-353F-8DBE-F27CB2FB4757),
  version(1.0),
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "_F")    

]
interface _F : IUnknown {
    HRESULT _stdcall foo([out, retval] long* pRetVal);
};

If there's no way to do this automatically now, I'll be writing something that uses reflection to do it, so I'd appreciate any recommendations on that side of things too. For example, are there any attributes that I need to check for on methods? On parameters? I dug into the IL of System.Runtime.InteropServices.TypeLibConverter to see how it does it, but unfortunately all the goodies are in the private internalcall nConvertAssemblyToTypeLib() function which I can't get at.

Thanks!

No correct solution

OTHER TIPS

The real question is what do you try to achieve?

From ClassInterface docs: "Indicates the type of class interface to be generated for a class exposed to COM, if an interface is generated at all."

The idea behind ClassInterface is to make available the programmatic interface of the class to COM clients - this is why you see full interface, including base class methods. The goal is not to provide a view to a subset of the class functionality. That's what interfaces are for (and ClassInterfaceType.None)

Providing a class interface without late binding capability (i.e. IDispatch) is something you shouldn't really do (and ClassInterface luckily doesn't enable it) as it is directly depending on the layout of your class to function properly. This means that neither the class nor its base classes should ever change. That is something which is hard to guarantee and why this is in general bad idea, besides the fact that it goes against whole philosophy of COM. Early-bound clients are tightly coupled to vtable layout of the interface - every change in the layout would break it. Late-bound clients don't exhibit this problem, as methods are resolved by name and they can handle missing method properly.

Using AutoDual already provides the early-bound access to the methods if you really need it, so I don't see why would you want to reinvent the wheel.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top