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

Dav*_*rno 5 com f# 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;
}
Run Code Online (Sandbox Code Playgroud)

我已经尝试将其转换为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
Run Code Online (Sandbox Code Playgroud)

然而,它报告该let shell = new Shell32.Shell()行的错误,说new不能在接口类型上使用.

我刚刚犯了一个愚蠢的语法错误,还是需要从F#访问COM所需的额外工作?

Han*_*ant 9

我对F#编译器知之甚少,但你的评论很明显.C#和VB.NET编译器对COM内置有相当多的显式支持.请注意,您的语句new在接口类型上使用运算符,interop库中的Shell32.Shell如下所示:

[ComImport]
[Guid("286E6F1B-7113-4355-9562-96B7E9D64C54")]
[CoClass(typeof(ShellClass))]
public interface Shell : IShellDispatch6 {}
Run Code Online (Sandbox Code Playgroud)

IShellDispatch6是真正的接口类型,您还可以通过IShellDispatch5接口查看IShellDispatch.这是过去20年工作中的版本控制,COM接口定义是不可变的,因为更改它们几乎总是在运行时导致无法识别的硬崩溃.

[CoClass]属性是这个故事的重要属性,这就是C#编译器new在[ComImport]接口类型上使用的内容.告诉它通过创建实例Shell32.ShellClass实例并获取Shell接口来创建对象.F#编译器没有做什么.

ShellClass是一个类,它是由类型库导入器自动生成的.COM从不公开具体类,它使用基于超纯接口的编程范例.对象总是由对象工厂创建,CoCreateInstance()是它的主力.它本身就是一个方便的功能,真正的工作是通过通用的IClassFactory界面,超纯粹的风格.每个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...
}
Run Code Online (Sandbox Code Playgroud)

大火和运动,不应该使用它们.唯一真正重要的是[Guid]属性,它提供了CoCreateInstance()所需的CLSID.它还需要接口声明提供的IID,接口的[Guid].

因此,F#中的解决方法是创建Shell32.ShellClass对象,就像C#编译器隐式执行一样.虽然从技术上讲,您可以将引用保留在ShellClass变量中,但您应该非常支持接口类型.COM方式,纯粹的方式,它避免了这种问题.最终,CLR完成了工作,它在new运算符实现中识别ShellClass类声明的[ClassInterface]属性..NET中更明确的方法是使用Type.GetTypeFromCLSID()和Activator.CreateInstance(),当你只拥有coclass的Guid时很方便.

  • 对于那些对此背后的机制感兴趣的人,Jon Skeet的博文,[伪造COM来欺骗C#编译器](https://codeblog.jonskeet.uk/2009/07/07/faking-com-to-fool-the- c-compiler /)从不同的角度讨论这个主题. (3认同)