在本机代码中通过COM 使用 F#
虽然大多数情况下,我们可能希望从 F# 代码调用本机代码,但是,在某些情况下,也可能想从本机代码中调用 F# 库函数。例如,假设我们有一个大型的程序是用 C++ 写的,有可能希望用户界面保持用 C++,而把一些逻辑,比如执行复杂数学计算的部分迁移到 F#,以方便维护。在这种情况下,我们就要从本机代码中调用 F# 了。简单的方法是借用.NET 提供的工具,为我们的 F# 程序集创建 COM 包装;然后,使用COM 运行时从 C++ 中调用 F# 函数。
要通过 COM 公开函数,需要用特殊方法进行开发。首先,必须定义接口,为函数指定契约,接口的成员必须使用命名参数(参见本章前面的“C# 中调用 F# 库”一节),接口本身使用 System.Runtime.InteropServices.Guid特性来标记;然后,必须提供一个类来实现这个接口,这要用System.Runtime.InteropServices.Guid 和System.Runtime.InteropServices.ClassInterface 特性进行标记,还应该总是把 ClassInterfaceType.None枚举成员传递给 ClassInterface 特性的构造函数,说明没有接口应该自动生成。
我们来看一下示例是如何做的。假设我们想公开两个函数Add 和 Sub 给非托管的客户端,需要在命名空间Strangelights 下创建接口IMath,然后,创建类 Math 实现这个接口,还需要保证类和接口用适当的特性进行标记。最后的代码可能像这样:
namespaceStrangelights
open System
open System.Runtime.InteropServices
// define an interface(since all COM classes must
// have a seperateinterface)
// mark it with afreshly generated Guid
[
]
type IMath =
abstract Add : x: int* y: int -> int
abstract Sub : x: int* y: int -> int
// implement theinterface, the class must:
// - have an emptyconstuctor
// - be marked with itsown guid
// - be marked with theClassInterface attribute
[
ClassInterface(ClassInterfaceType.None)>]
typeMath() =
interface IMath with
memberthis.Add(x, y) = x + y
memberthis.Sub(x, y) = x - y
函数 Add 和 Sub很简单,因此,直接在 Math 类的主体中实现,自然没有问题;但是,如果需要把它们拆分到类之外的其他助理函数,也不会有问题,可以用自己觉得合适的任何方法实现类成员,都行,只需要提供接口和类,这样,COM 运行时就能够在代码中有一个入口点。
下面是这个过程中公开的最复杂的部分,注册程序集,使 COM 运行时能找到它。这是通过使用工具RegAsm.exe 实现的。假设我们把前面的示例代码编译成的 .NET .dll 通过OM 需要保证类的s 叫 ComLibrary.dll,那么,需要调用 RegAsm.exe两次,使用下面的命令:
regasm comlibrary.dll /tlb:comlibrary.tlb
regasm comlibrary.dll
第一次创建类型库文件 .tlb,它是能够用于开发的 C++ 项目中的;第二次是注册程序集,使 COM 运行时能够找到它;这两个步骤还需要在分发程序集的机器上运行。
C++ 调用 Add 函数是这样的,开发环境以及如何设置 C++ 编译器都会对代码的编译产生影响。在这里,我们创建的Visual Studio 项目,选择了控制台应用程序模板,并启用了 ATL。注意,下面是有关源代码的描述:

#impZ??http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcnQgw/zB7rjmy9+x4NLrxvfQ6NKqtbzI687Sw8fX1Ly6tcTA4NDNv+KjrL/JxNzQ6NKqyrnTw87EvP6jqC50bGKjqbXEzerV+8K3vraju7Hg0uvG97u5vavX1LavyfqzydK7uPbNt87EvP6jrNTa1eLA78rHY29tbGlicmFyeS50bGijrM7EvP61xM671sPU2mRlYnVnILvyIHJlbGVhc2UgxL/CvM/CoaPL/LXE1/fTw8rHyMPO0sPH1qq1wMDg0M2/4tbQv8nTw7XEuq/K/brNserKtrf7o7s8L3A+CjxwPsi7uvOjrNDo0qqz9cq8u68gQ09NINTL0NDKsaOs1eLKx82ouf2199PDQ29Jbml0aWFsaXplILqvyv3Ktc/WtcSjuzwvcD4KPHA+vdPXxaOs0OjSqsn5w/fSu7j21rjV66Os1rjP8s7Sw8e0tL2otcS907/aIElNYXRoo6zV4srHzai5/bT6wutjb21saWJyYXJ5OjpJTWF0aFB0ciBwRG90TmV0Q09NUHRyOyDN6rPJtcSho9ei0uKjrMP8w/u/1bzkysfAtNfUv+K1xMP719ajrLb4srvKxyAuTkVUILXEw/zD+7/VvOSjuzwvcD4KPHA+z8LSu7K9o6zQ6NKqtLS9qCBNYXRoIMDgtcTKtcD9o6zV4srHzai5/bX308NDcmVhdGVJbnN0YW5jZSC3vbeozeqzybXEo6yw0SBNYXRoIMDgtcRHVUlEILSrtd24+Mv8oaPQ0tTLtcTKx6Oszqq0y8S/tcSjrNPQ0ru49rOjwb+2qNLlo7s8L3A+CjxwPsjnufvV4tCptryzybmmwcujrL7NxNzD+7X308MgQWRkILqvyv3By6Gj16LS4qOsuq/K/bXEt7W72Le1u9hkZCDEv7XEo6zT0NK7uPazo8G/1eIgciBwRG90TmV0Q09NUHRyOyDKtbzKyc/KxyBIUkVTVUxUo6zV4rj2JiMyMDU0MDu45svfztLDx7X308PKx7fx0tG+rbPJuaajrLb4yrW8yrXEyrW8yr3hufvKx82ouf3Su7j2yuSz9rLOyv20q7P2wLS1xKGjPC9wPgo8cD4gPC9wPgo8cD48ZW0+Ly8gISEhIEMmIzQzOyYjNDM7IFNvdXJjZSAhISE8L2VtPjwvcD4KPHA+I2luY2x1ZGUg"stdafx.h"
// import the meta data about out.NET/COM library
#import "..\ComLibrary\ComLibra