Exposición de los componentes de .NET Core a COM

En este artículo se explica cómo exponer una clase a COM desde .NET Core (o .NET 5+). En este tutorial se le enseñará a hacer lo siguiente:

  • Exponer una clase a COM desde .NET Core.
  • Generar un servidor COM como parte de la creación de la biblioteca de .NET Core.
  • Generar automáticamente un manifiesto de servidor en paralelo para COM sin registro.

Requisitos previos

Crear la biblioteca

El primer paso consiste en crear la biblioteca.

  1. Cree una carpeta nueva y, en ella, ejecute el siguiente comando:

    dotnet new classlib
    
  2. Abra Class1.cs.

  3. Agregue using System.Runtime.InteropServices; a la parte superior del archivo.

  4. Cree una interfaz denominada IServer. Por ejemplo:

    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. Agregue el atributo [Guid("<IID>")] a la interfaz con el GUID de la interfaz para la interfaz COM que está implementando. Por ejemplo: [Guid("fe103d6e-e71b-414c-80bf-982f18f6c1c7")]. Tenga en cuenta que este GUID debe ser único, ya que es el único identificador de esta interfaz para COM. En Visual Studio, puede generar un GUID desde Herramientas > Crear GUID para abrir la herramienta de creación de GUID.

  6. Agregue el atributo [InterfaceType] a la interfaz y especifique qué interfaces COM base debe implementar la interfaz.

  7. Cree una clase denominada Server que implemente IServer.

  8. Agregue el atributo [Guid("<CLSID>")] a la clase, con el identificador de clase GUID para la clase COM que está implementando. Por ejemplo: [Guid("9f35b6f5-2c05-4e7f-93aa-ee087f6e7ab6")]. Igual que sucede con la interfaz GUID, este GUID debe ser único, ya que es el único identificador de esta interfaz para COM.

  9. Agregue el atributo [ComVisible(true)] a la interfaz y a la clase.

Importante

A diferencia de lo que ocurre en .NET Framework, en .NET Core debe especificar el CLSID de cualquier clase que le interese que se pueda activar mediante COM.

Generar el host de COM

  1. Abra el archivo de proyecto .csproj y agregue <EnableComHosting>true</EnableComHosting> en una etiqueta <PropertyGroup></PropertyGroup>.
  2. Compile el proyecto.

El resultado contendrá un archivo ProjectName.dll, ProjectName.deps.json, ProjectName.runtimeconfig.json y ProjectName.comhost.dll.

Registrar el host COM para COM

Abra un símbolo del sistema con privilegios elevados y ejecute regsvr32 ProjectName.comhost.dll. Esto registrará todos los objetos .NET expuestos con COM.

Habilitar RegFree COM

  1. Abra el archivo de proyecto .csproj y agregue <EnableRegFreeCom>true</EnableRegFreeCom> en una etiqueta <PropertyGroup></PropertyGroup>.
  2. Compile el proyecto.

Ahora el resultado también contendrá un archivo ProjectName.X.manifest. Este archivo es el manifiesto en paralelo que se podrá usar con COM sin registro.

Inserción de bibliotecas de tipos en el host COM

A diferencia de .NET Framework, en .NET Core o .NET 5+ no se admite la generación de una biblioteca de tipos COM (TLB) a partir de un ensamblado de .NET. La instrucción es escribir manualmente un archivo IDL o un encabezado C/C++ para las declaraciones nativas de las interfaces COM. Si decide escribir un archivo IDL, puede compilarlo con el compilador MIDL del SDK de Visual C++ para generar un TLB.

En .NET 6 y versiones posteriores, el SDK de .NET admite la inserción de los TLB ya compilados en el host COM como parte de la compilación del proyecto.

Para insertar una biblioteca de tipos en la aplicación, siga estos pasos:

  1. Abra el archivo del proyecto .csproj y agregue <ComHostTypeLibrary Include="path/to/typelib.tlb" Id="<id>" /> en una etiqueta <ItemGroup></ItemGroup>.
  2. Reemplace <id> por un valor entero positivo. El valor debe ser único entre los TLB que especifique para que se inserte en el host COM.
    • El atributo Id es opcional si solo agrega una instancia de ComHostTypeLibrary al proyecto.

Por ejemplo, en el siguiente bloque de código se agrega la biblioteca de tipos Server.tlb en el índice 1 al host COM:

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

Carga en el valor predeterminado AssemblyLoadContext

Durante la activación, el ensamblado que contiene el componente COM se carga en un elemento AssemblyLoadContext independiente en función de la ruta del ensamblado. Si hay un ensamblado que proporciona varios servidores COM, se reutiliza AssemblyLoadContext de modo que todos los servidores de ese ensamblado residan en el mismo contexto de carga. Si hay varios ensamblados que proporcionan servidores COM, se crea un nuevo elemento AssemblyLoadContext para cada ensamblado, y cada servidor reside en el contexto de carga correspondiente a su ensamblado.

En .NET 8 y versiones posteriores, el ensamblado puede especificar que se debe cargar en el valor predeterminado AssemblyLoadContext. Para habilitar la carga en el contexto predeterminado, agregue el elemento RuntimeHostConfigurationOption siguiente al proyecto:

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

Ejemplo

Puede encontrar un ejemplo de servidor COM totalmente funcional en el repositorio dotnet/samples de GitHub.

Notas adicionales

Importante

En .NET Framework, tanto los clientes de 32 bits como los de 64 bits pueden consumir un ensamblado "Cualquier CPU". De forma predeterminada, en .NET Core, .NET 5 y versiones posteriores, los ensamblados «Cualquier CPU» van acompañados de un archivo .comhost.dll de 64 bits. Por este motivo, solo los clientes de 64 bits pueden consumirlos. Es el valor predeterminado porque es lo que representa el SDK. Este comportamiento es idéntico al modo en el que se publica la característica "autocontenida": de forma predeterminada, usa lo que proporciona el SDK. La propiedad NETCoreSdkRuntimeIdentifier de MSBuild determina el valor de bits de *.comhost.dll. En realidad, la parte administrada ignora el valor de bits, como se espera, pero el valor predeterminado del recurso nativo asociado es el SDK de destino.

No se admiten implementaciones autocontenidas de componentes COM. Solo se admiten las implementaciones dependientes del marco de los componentes COM.

Además, la carga de .NET Framework y .NET Core en el mismo proceso presenta limitaciones de diagnóstico. La limitación principal es la depuración de componentes administrados, ya que no es posible depurar .NET Framework y .NET Core al mismo tiempo. Asimismo, las dos instancias en tiempo de ejecución no comparten ensamblados administrados. Esto significa que no es posible compartir tipos de .NET reales entre los dos tiempos de ejecución, por lo que todas las interacciones deben restringirse a los contratos de interfaz COM expuestos.