如何将使用Shell COM的C#代码转换为F#?

时间:2016-05-10 17:29:49

标签: f# com c#-to-f#

我有以下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?

1 个答案:

答案 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时才能使用。