向 COM 公开 .NET Core 组件

本文分步介绍如何将类从 .NET Core(或 .NET 5+)公开到 COM。 本教程介绍了如何:

  • 从 .NET Core 向 COM 公开类。
  • 生成 COM 服务器作为构建 .NET Core 库的一部分。
  • 自动为无注册表 COM 生成并行服务器清单。

先决条件

创建库

第一步是创建库。

  1. 创建新文件夹,并在该文件夹中运行以下命令:

    dotnet new classlib
    
  2. 打开 Class1.cs

  3. using System.Runtime.InteropServices; 添加到文件顶部。

  4. 创建名为 IServer 的接口。 例如:

    using System;
    using System.Runtime.InteropServices;
    
    [ComVisible(true)]
    [Guid(ContractGuids.ServerInterface)]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IServer
    {
        /// <summary>
        /// Compute the value of the constant Pi.
        /// </summary>
        double ComputePi();
    }
    
  5. [Guid("<IID>")] 属性添加到接口,包含要实现的 COM 接口的接口 GUID。 例如 [Guid("fe103d6e-e71b-414c-80bf-982f18f6c1c7")]。 请注意,此 GUID 必须唯一,因为它是 COM 的此接口的唯一标识符。 在 Visual Studio 中,可通过转到“工具”>“创建 GUID”,打开“创建 GUID”工具来生成 GUID。

  6. [InterfaceType] 属性添加到接口,并指定接口应实现的基本 COM 接口。

  7. 创建用于实现 IServer 的名为 Server 的类。

  8. [Guid("<CLSID>")] 属性添加到类,包含要实现的 COM 类的类标识符 GUID。 例如 [Guid("9f35b6f5-2c05-4e7f-93aa-ee087f6e7ab6")]。 与接口 GUID 一样,此 GUID 必须唯一,因为它是 COM 的此接口的唯一标识符。

  9. [ComVisible(true)] 属性添加到接口和类。

重要

与 .NET Framework 不同,.NET Core 要求指定想要通过 COM 激活的任何类的 CLSID。

生成 COM 主机

  1. 打开 .csproj 项目文件并在 <PropertyGroup></PropertyGroup> 标记中添加 <EnableComHosting>true</EnableComHosting>
  2. 生成项目。

生成的输出将具有 ProjectName.dllProjectName.deps.jsonProjectName.runtimeconfig.jsonProjectName.comhost.dll 文件。

为 COM 注册 COM 主机

打开提升的命令提示符,然后运行 regsvr32 ProjectName.comhost.dll。 这将使用 COM 注册所有公开的 .NET 对象。

启用 RegFree COM

  1. 打开 .csproj 项目文件并在 <PropertyGroup></PropertyGroup> 标记中添加 <EnableRegFreeCom>true</EnableRegFreeCom>
  2. 生成项目。

生成的输出现在还将具有 ProjectName.X.manifest 文件。 此文件是用于无注册表的 COM 的并行清单。

在 COM 主机中嵌入类型库

与 .NET Framework 不同,.NET Core 或 .NET 5+ 中不支持从 .NET 程序集生成 COM 类型库 (TLB)。 本指南旨在说明如何为 COM 接口的本机声明手动编写 IDL 文件或 C/C++ 标头。 如果决定编写 IDL 文件,可使用 Visual C++ SDK 的 MIDL 编译器对其进行编译来生成 TLB。

在 .NET 6 及更高版本中,.NET SDK 支持在项目生成过程中将已编译的 TLB 嵌入 COM 主机。

若要在应用程序中嵌入类型库,请执行以下步骤:

  1. 打开 .csproj 项目文件并在 <ItemGroup></ItemGroup> 标记中添加 <ComHostTypeLibrary Include="path/to/typelib.tlb" Id="<id>" />
  2. <id> 替换为正整数值。 在指定要嵌入 COM 主机的 TLB 中,该值必须是唯一的。
    • 如果你只将一个 Id 添加到项目中,则 ComHostTypeLibrary 属性是可选的。

例如,以下代码块将索引为 1Server.tlb 类型库添加到 COM 主机:

<ItemGroup>
    <ComHostTypeLibrary Include="Server.tlb" Id="1" />
</ItemGroup>

在默认 AssemblyLoadContext 中加载

在激活期间,包含 COM 组件的程序集将基于程序集路径加载到单独的 AssemblyLoadContext 中。 如果有一个程序集提供多个 COM 服务器,则重用 AssemblyLoadContext,以便该程序集中的所有服务器都位于同一加载上下文中。 如果有多个程序集提供 COM 服务器,则将为每个程序集创建一个新的 AssemblyLoadContext,并且每个服务器位于其程序集所对应的加载上下文中。

在 .NET 8 及更高版本中,程序集可以指定应在默认 AssemblyLoadContext 中加载它。 若要在默认上下文中启用加载,请将以下 RuntimeHostConfigurationOption 项添加到项目:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Runtime.InteropServices.COM.LoadComponentInDefaultContext" Value="true" />
</ItemGroup>

示例

GitHub 上的 dotnet/samples 存储库中有一个正常运行的 COM 服务器示例

附加说明

重要

在 .NET Framework 中,32 位和 64 位客户端可以使用“任何 CPU”程序集。 默认情况下,在 .NET Core、.NET 5 和更高版本中,“任何 CPU”程序集附带了 64 位的 *.comhost.dll。 因此,它们只能由 64 位客户端使用。 这是默认的,因为这是 SDK 表示的内容。 此行为与发布“自包含”功能的方式相同:默认情况下,它使用 SDK 提供的内容。 NETCoreSdkRuntimeIdentifier MSBuild 属性确定 *.comhost.dll 的位数。 正如预期的那样,托管部分的位数实际上是不可知的,而随附的本机资产默认为目标 SDK。

不支持 COM 组件的自包含部署。 仅支持 COM 组件的依赖框架的部署

此外,将 .NET Framework 和 .NET Core 同时加载到同一进程具有诊断限制。 主要限制是调试托管组件,因为不能同时调试 .NET Framework 和 .NET Core。 此外,这两个运行时实例不共享托管程序集。 这意味着无法在两个运行时之间共享实际的 .NET 类型,所有交互必须仅限于公开的 COM 接口协定。