如何将使用 Shell COM 的 C# 代码转换为 F#?
How to convert C# code that uses Shell COM 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?
我对 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# 编译器要查找的内容,您在 [ComImport] 接口类型上使用 new
。告诉它通过创建 Shell32.ShellClass
实例的实例并获取 Shell 接口来创建对象。 F# 编译器不执行的操作。
ShellClass
是一个 fake class,它是由类型库导入程序自动生成的。 COM 从不公开具体的 classes,它使用基于超纯接口的编程范例。对象总是由对象工厂创建的,CoCreateInstance() is the workhorse for that. Itself a convenience function, the real work is done by the universal 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...
}
大量的火焰和移动,none 应该永远使用。唯一 真正 重要的是 [Guid] 属性,它提供 CoCreateInstance() 需要的 CLSID。它还需要IID,即接口的[Guid],由接口声明提供。
因此 F# 中的解决方法是创建 Shell32.ShellClass 对象,就像 C# 编译器隐式执行的那样。虽然从技术上讲,您可以将引用保存在 ShellClass 变量中,但您应该强烈支持接口类型。 COM方式,纯粹的方式,它避免了this kind of problem。最终是 CLR 完成了工作,它在其 new
运算符实现中识别 ShellClass class 声明中的 [ClassInterface] 属性. .NET 中更明确的方法是使用 Type.GetTypeFromCLSID() 和 Activator.CreateInstance(),当您只有 coclass.
的 Guid 时很方便
我有以下 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?
我对 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# 编译器要查找的内容,您在 [ComImport] 接口类型上使用 new
。告诉它通过创建 Shell32.ShellClass
实例的实例并获取 Shell 接口来创建对象。 F# 编译器不执行的操作。
ShellClass
是一个 fake class,它是由类型库导入程序自动生成的。 COM 从不公开具体的 classes,它使用基于超纯接口的编程范例。对象总是由对象工厂创建的,CoCreateInstance() is the workhorse for that. Itself a convenience function, the real work is done by the universal 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...
}
大量的火焰和移动,none 应该永远使用。唯一 真正 重要的是 [Guid] 属性,它提供 CoCreateInstance() 需要的 CLSID。它还需要IID,即接口的[Guid],由接口声明提供。
因此 F# 中的解决方法是创建 Shell32.ShellClass 对象,就像 C# 编译器隐式执行的那样。虽然从技术上讲,您可以将引用保存在 ShellClass 变量中,但您应该强烈支持接口类型。 COM方式,纯粹的方式,它避免了this kind of problem。最终是 CLR 完成了工作,它在其 new
运算符实现中识别 ShellClass class 声明中的 [ClassInterface] 属性. .NET 中更明确的方法是使用 Type.GetTypeFromCLSID() 和 Activator.CreateInstance(),当您只有 coclass.