向 COM 公开 .NET Core 组件
本文分步介绍如何将类从 .NET Core(或 .NET 5+)公开到 COM。 本教程介绍了如何:
- 从 .NET Core 向 COM 公开类。
- 生成 COM 服务器作为构建 .NET Core 库的一部分。
- 自动为无注册表 COM 生成并行服务器清单。
先决条件
- 安装 .NET Core 3.0 SDK 或更高版本。
创建库
第一步是创建库。
创建新文件夹,并在该文件夹中运行以下命令:
dotnet new classlib
打开
Class1.cs
。将
using System.Runtime.InteropServices;
添加到文件顶部。创建名为
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(); }
将
[Guid("<IID>")]
属性添加到接口,包含要实现的 COM 接口的接口 GUID。 例如[Guid("fe103d6e-e71b-414c-80bf-982f18f6c1c7")]
。 请注意,此 GUID 必须唯一,因为它是 COM 的此接口的唯一标识符。 在 Visual Studio 中,可通过转到“工具”>“创建 GUID”,打开“创建 GUID”工具来生成 GUID。将
[InterfaceType]
属性添加到接口,并指定接口应实现的基本 COM 接口。创建用于实现
IServer
的名为Server
的类。将
[Guid("<CLSID>")]
属性添加到类,包含要实现的 COM 类的类标识符 GUID。 例如[Guid("9f35b6f5-2c05-4e7f-93aa-ee087f6e7ab6")]
。 与接口 GUID 一样,此 GUID 必须唯一,因为它是 COM 的此接口的唯一标识符。将
[ComVisible(true)]
属性添加到接口和类。
重要
与 .NET Framework 不同,.NET Core 要求指定想要通过 COM 激活的任何类的 CLSID。
生成 COM 主机
- 打开
.csproj
项目文件并在<PropertyGroup></PropertyGroup>
标记中添加<EnableComHosting>true</EnableComHosting>
。 - 生成项目。
生成的输出将具有 ProjectName.dll
、ProjectName.deps.json
、ProjectName.runtimeconfig.json
和 ProjectName.comhost.dll
文件。
为 COM 注册 COM 主机
打开提升的命令提示符,然后运行 regsvr32 ProjectName.comhost.dll
。 这将使用 COM 注册所有公开的 .NET 对象。
如果打算嵌入类型库 (TLB),建议也使用 ComRegisterFunctionAttribute
和 ComUnregisterFunctionAttribute
定义函数。 这些函数可用于注册和注销 COM 服务器的 TLB。 有关完整的示例,请参阅 OutOfProcCOM
示例。
启用 RegFree COM
- 打开
.csproj
项目文件并在<PropertyGroup></PropertyGroup>
标记中添加<EnableRegFreeCom>true</EnableRegFreeCom>
。 - 生成项目。
生成的输出现在还将具有 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 主机。
若要在应用程序中嵌入类型库,请执行以下步骤:
- 打开
.csproj
项目文件并在<ItemGroup></ItemGroup>
标记中添加<ComHostTypeLibrary Include="path/to/typelib.tlb" Id="<id>" />
。 - 将
<id>
替换为正整数值。 在指定要嵌入 COM 主机的 TLB 中,该值必须是唯一的。- 如果你只将一个
Id
添加到项目中,则ComHostTypeLibrary
属性是可选的。
- 如果你只将一个
例如,以下代码块将索引为 1
的 Server.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 组件的依赖框架的部署。
不支持通过 EnableComHosting 属性从 C++/CLI 项目公开 COM 组件。
此外,将 .NET Framework 和 .NET Core 同时加载到同一进程具有诊断限制。 主要限制是调试托管组件,因为不能同时调试 .NET Framework 和 .NET Core。 此外,这两个运行时实例不共享托管程序集。 这意味着无法在两个运行时之间共享实际的 .NET 类型,所有交互必须仅限于公开的 COM 接口协定。