.NET 中的System.Runtime.InteropServices.ClassInterfaceType枚举有一个AutoDispatch
值和一个AutoDual
值,但没有AutoUnknown
值。为什么不呢,有没有人已经想出了一种相当自动化的方法来完成它,这样我就不必重新发明轮子了?
对于更多背景知识,以下是当前枚举中的三个值以及它们的作用:
ClassInterfaceType.None
不会为您创建类接口。这是 Microsoft 的推荐值。如果没有类接口,您必须使用您想要的任何成员创建自己的接口,并让您的类实现它。然后,您可以在接口上使用System.Runtime.InteropServices.ClassInterfaceAttribute属性使其成为分派接口(支持后期绑定客户端)、未知接口(支持早期绑定客户端)或双重接口(支持早期绑定客户端和后期绑定客户端)。ClassInterfaceType.AutoDispatch
创建一个派生自IDispatch
没有显式成员的类接口。由于没有显式成员,它只能支持后期绑定的调用者。ClassInterfaceType.AutoDual
创建一个派生自该类接口的类接口,IDispatch
该接口确实具有该类的所有公共非静态成员以及其基类和任何已实现接口的所有成员的显式成员。Microsoft 强烈反对使用这个值,“因为版本限制”,因为如果类的任何成员发生变化,类接口也会发生变化。(因此,在类更改后不重新绑定的调用者最终可能会调用错误的方法或传递不正确的参数。)
所以我想知道的是为什么没有一个调用的值ClassInterfaceType.AutoUnknown
会创建一个派生自该类接口的类接口,该接口IUnknown
明确声明您的类的成员(不是基类或它实现的其他接口)。
虽然我们在这里,但我也希望有ClassInterfaceType.AutoDualDirect
类似 AutoDual 的东西,除了它不会公开基类中的所有成员和所有实现的接口,它只会公开类的直接成员,因为其他成员可以通过其他 COM 接口检索。
那么这里的故事是什么,我错过了什么?我知道微软表示,由于“版本控制挑战”,它建议不要使用 AutoDual,并且这些担忧在某种程度上也适用于 AutoUnknown。但尽管有这个建议,他们仍然有AutoDual。为什么他们也没有 AutoUnknown?
关于那些“版本控制挑战”的一句话:这些问题中最危险的方面只适用于后期绑定的调用者。AutoUnknown 将生成一个 -IUnknown
派生接口,而不是 -IDispatch
派生接口,因此我们只需要关注早期绑定客户端可能遇到的版本控制问题。并且由于任何自动生成的类接口的 IID 都会在类的公共非静态成员发生变化时发生变化,因此早期绑定的客户端将无法对现有成员进行“错误调用”,它只会失败 QueryInterface( ) 用于类接口。仍然是失败,但可以管理,而不是崩溃或任何事情。这甚至没有打破不允许接口改变的 COM 规则;它没有改变,它正在成为一个新接口(新 IID)。的确,IUnknown
AutoDual 机制的一半(减去派生成员)。事实上,它比 AutoDual 遇到的“版本控制挑战”更少,因为 AutoDual 也有所有后期绑定的挑战。
所以我不得不再次问:如果 AutoUnknown 不存在,为什么 AutoDual 存在!
为了给出一些实际示例,请考虑以下 C# 代码:
using System.Runtime.InteropServices;
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class F
{
public int foo()
{
return 5;
}
}
生成以下 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);
};
很好,但我建议 AutoUnknown 将有助于生成此 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(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);
};
如果现在没有办法自动执行此操作,我将编写一些使用反射来执行此操作的内容,因此我也会很感激有关这方面的任何建议。例如,我需要检查方法上的任何属性吗?关于参数?我深入研究了 ILSystem.Runtime.InteropServices.TypeLibConverter
以了解它是如何做到的,但不幸的是,所有好东西都在我无法使用的私有内部调用 nConvertAssemblyToTypeLib() 函数中。
谢谢!