我对 F# 编译器了解不够,但您的评论使它足够明显。 C# 和 VB.NET 编译器对 COM 内置提供了大量显式支持。请注意,您的语句使用new
接口类型上的运算符,互操作库中的 Shell32.Shell 如下所示:
[ComImport]
[Guid("286E6F1B-7113-4355-9562-96B7E9D64C54")]
[CoClass(typeof(ShellClass))]
public interface Shell : IShellDispatch6 {}
IShellDispatch6是真正的接口类型,通过IShellDispatch5接口也可以看到IShellDispatch。这是过去 20 年的版本控制在起作用,COM 接口定义是不可变的,因为更改它们几乎总是会导致运行时无法诊断的硬崩溃。
[CoClass] 属性是本故事的重要属性,这就是 C# 编译器寻找您使用的属性new
在 [ComImport] 接口类型上。告诉它通过创建实例来创建对象Shell32.ShellClass
实例并获取Shell接口。 F# 编译器不执行的操作。
ShellClass
is a fake类,它是由类型库导入器自动生成的。 COM 从不公开具体的类,它使用超纯的基于接口的编程范例。对象总是由对象工厂创建,CoCreateInstance() https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615%28v=vs.85%29.aspx是这方面的主力。本身就是一个便利功能,真正的工作是由通用来完成的IClassFactory接口 https://msdn.microsoft.com/en-us/library/windows/desktop/ms694364%28v=vs.85%29.aspx,超纯粹风格。每个 COM 组件类都实现其 CreateInstance() 方法。
类型库导入器使 ShellClass 看起来像这样:
[ComImport]
[TypeLibType(TypeLibTypeFlags.FCanCreate)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("13709620-C279-11CE-A49E-444553540000")]
public class ShellClass : IShellDispatch6, Shell {
// Methods
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime), DispId(0x60040000)]
public virtual extern void AddToRecent([In, MarshalAs(UnmanagedType.Struct)] object varFile, [In, Optional, MarshalAs(UnmanagedType.BStr)] string bstrCategory);
// Etc, many more methods...
}
大量的火力和运动,任何一个都不应该被使用。唯一的一点就是really重要的是 [Guid] 属性,它提供 CoCreateInstance() 所需的 CLSID。它还需要 IID,即接口的 [Guid],由接口声明提供。
因此,F# 中的解决方法是创建 Shell32.ShellClass 对象,就像 C# 编译器隐式执行的操作一样。虽然从技术上讲,您可以将引用保留在 ShellClass 变量中,但您应该强烈支持接口类型。 COM方式,纯粹的方式,它避免了这种问题 https://stackoverflow.com/questions/5170078/net-document-write-with-mshtml。最终是 CLR 完成了这项工作,它识别其 ShellClass 类声明中的 [ClassInterface] 属性。new
运营商实施。 .NET 中更明确的方法是使用 Type.GetTypeFromCLSID() 和 Activator.CreateInstance(),当您只有 coclass 的 Guid 时,这很方便。