我有以下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所需的额外工作?
我对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时很方便.