我有以下C#方法:
private static bool IsLink(string shortcutFilename)
{
var pathOnly = Path.GetDirectoryName(shortcutFilename);
var filenameOnly = Path.GetFileName(shortcutFilename);
var shell = new Shell32.Shell();
var folder = shell.NameSpace(pathOnly);
var folderItem = folder.ParseName(filenameOnly);
return folderItem != null && folderItem.IsLink;
}
我尝试将此转换为F#:
let private isLink filename =
let pathOnly = Path.GetDirectoryName(filename)
let filenameOnly = Path.GetFileName(filename)
let shell = new Shell32.Shell()
let folder = shell.NameSpace(pathOnly)
let folderItem = folder.ParseName(filenameOnly)
folderItem <> null && folderItem.IsLink
然而,它报告了let shell = new Shell32.Shell()
行的错误,指出new
不能用于接口类型。
我刚刚犯了一个愚蠢的语法错误,还是需要额外的工作才能从F#访问COM?
答案 0 :(得分:9)
我对F#编译器的了解不够,但你的评论很明显。 C#和VB.NET编译器对COM内置有相当多的显式支持。请注意,您的语句在接口类型上使用new
运算符,interop库中的Shell32.Shell如下所示:
[ComImport]
[Guid("286E6F1B-7113-4355-9562-96B7E9D64C54")]
[CoClass(typeof(ShellClass))]
public interface Shell : IShellDispatch6 {}
IShellDispatch6是真正的接口类型,您还可以通过IShellDispatch5接口查看IShellDispatch。在过去20年的工作版本中,COM接口定义是不可变的,因为更改它们几乎总是会在运行时导致不可识别的硬崩溃。
[CoClass]属性是这个故事的重要属性,C#编译器在[ComImport]接口类型上使用new
寻找的是什么。告诉它通过创建Shell32.ShellClass
实例的实例来创建对象并获取Shell接口。 F#编译器没有做什么。
ShellClass
是伪类,它是由类型库导入程序自动生成的。 COM从不公开具体类,它使用基于超纯接口的编程范例。对象始终由对象工厂创建,CoCreateInstance()是其中的主力。它本身就是一种便利功能,真正的工作是通过普遍的IClassFactory interface,超纯粹的风格来完成的。每个COM coclass都实现其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...
}
大量火灾和运动,不应该使用它们。 真正唯一重要的是[Guid]属性,它提供了CoCreateInstance()所需的CLSID。它还需要接口声明提供的IID,接口的[Guid]。
因此,F#中的解决方法是创建Shell32.ShellClass对象,就像C#编译器隐式执行一样。虽然从技术上讲,您可以将引用保留在ShellClass变量中,但您应该非常支持接口类型。 COM方式,纯粹的方式,它避免了this kind of problem。最终,CLR完成了工作,它在new
运算符实现中识别ShellClass类声明的[ClassInterface]属性。 .NET中更明确的方法是使用Type.GetTypeFromCLSID()和Activator.CreateInstance(),只有当你拥有coclass的Guid时才能使用。