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 fromIDispatch
with no explicit members. With no explicit members it can only support late-bound callers.ClassInterfaceType.AutoDual
creates a class interface derived fromIDispatch
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!