Compartir a través de


Generación de una proyección de C# a partir de un componente de C++/WinRT, distribuir como NuGet para aplicaciones .NET

En este tema, se explica el uso de C#/WinRT para generar un ensamblado de proyección de .NET (o interoperabilidad) de C# a partir de un componente de Windows Runtime de C++/WinRT y distribuirlo como un paquete NuGet para aplicaciones .NET.

En .NET 6 y versiones posteriores, ya no se admite el consumo de archivos de metadatos de Windows (WinMD) (consulte , donde se explica que se ha eliminado la compatibilidad integrada con WinRT en .NET). En su lugar, la herramienta C#/WinRT se puede usar para generar un ensamblado de proyección para cualquier archivo WinMD, que luego permite el consumo de componentes de WinRT desde aplicaciones .NET. Un ensamblaje de proyección también se conoce como ensamblaje de interoperabilidad. En este tutorial se muestra cómo hacer lo siguiente:

Prerrequisitos

Este tutorial y el ejemplo correspondiente requieren las siguientes herramientas y componentes:

  • Visual Studio 2022 (o Visual Studio 2019) con la carga de trabajo de desarrollo de la Plataforma Universal de Windows instalada. En Detalles de Instalación>Desarrollo de la Plataforma Universal de Windows, active la opción Herramientas de la Plataforma Universal de Windows (v14x) de C++.
  • SDK de .NET 6.0 o posterior.

Visual Studio 2019 solo. El extensión VSIX de C++/WinRT, que proporciona plantillas de proyecto de C++/WinRT en Visual Studio. Las plantillas de proyecto están integradas en Visual Studio 2022.

Usaremos Visual Studio 2022 y .NET 6 en este tutorial.

Importante

Además, deberá descargar o clonar el código de ejemplo de este tema desde la ejemplo de proyección de C#/WinRT en GitHub. Visite CsWinRTy haga clic en el botón verde Code para obtener la dirección URL de git clone. Asegúrese de leer el archivo README.md del ejemplo.

Crear un componente sencillo de Windows Runtime de C++/WinRT

Para seguir este tutorial, primero debes tener un componente de Windows Runtime (CMR) de C++/WinRT desde el cual generar el ensamblado de proyección en C#.

En este tutorial se usa el SimpleMathComponent WRC del ejemplo de proyección de C#/WinRT en GitHub, que ya haya descargado o clonado. SimpleMathComponent se creó a partir del componente de Windows Runtime de (C++/WinRT) plantilla de proyecto de Visual Studio (que viene con Visual Studio 2022 o con la extensión VSIX de C++/WinRT).

Para abrir el proyecto SimpleMathComponent en Visual Studio, abra el \CsWinRT\src\Samples\NetProjectionSample\CppWinRTComponentProjectionSample.sln archivo , que encontrará en la descarga o clonación del repositorio.

El código de este proyecto proporciona la funcionalidad de las operaciones matemáticas básicas que se muestran en el archivo de encabezado siguiente.

// SimpleMath.h
...
namespace winrt::SimpleMathComponent::implementation
{
    struct SimpleMath: SimpleMathT<SimpleMath>
    {
        SimpleMath() = default;
        double add(double firstNumber, double secondNumber);
        double subtract(double firstNumber, double secondNumber);
        double multiply(double firstNumber, double secondNumber);
        double divide(double firstNumber, double secondNumber);
    };
}

Puedes confirmar que la propiedad Windows Desktop Compatible está establecida en para el proyecto de componentes simpleMathComponent C++/WinRT de Windows Runtime. Para hacer eso, en las propiedades del proyecto de SimpleMathComponent, en Configuration Properties>General>Project Defaults, configure la propiedad Windows Desktop Compatible a . Esto garantiza que los archivos binarios en tiempo de ejecución correctos se carguen para consumir aplicaciones de escritorio de .NET.

página de propiedades compatible con Desktop

Para obtener pasos más detallados sobre cómo crear un componente de C++/WinRT y generar un archivo WinMD, consulta Componentes de Windows Runtime con C++/WinRT.

Nota:

Si va a implementar IInspectable::GetRuntimeClassName en su componente, entonces deberá devolver un nombre de clase WinRT válido. Dado que C#/WinRT usa la cadena de nombre de clase para la interoperabilidad, un nombre de clase en tiempo de ejecución incorrecto generará una excepción InvalidCastException.

Adición de un proyecto de proyección a la solución de componentes

En primer lugar, con la solución de CppWinRTComponentProjectionSample de , quite el proyecto de simpleMathProjection de de esa solución. A continuación, elimine del sistema de archivos la carpeta SimpleMathProjection (o cámbiele el nombre si lo prefiere). Estos pasos son necesarios para poder seguir este tutorial paso a paso.

  1. Agregue un nuevo proyecto de biblioteca de C# a la solución.

    1. En Explorador de Soluciones, haga clic con el botón derecho en su nodo de solución y seleccione Agregar>Nuevo Proyecto.
    2. En el cuadro de diálogo Agregar un nuevo proyecto, escriba Biblioteca de Clases en el cuadro de búsqueda. Elija C# en la lista de lenguajes y, a continuación, elija Windows en la lista de plataformas. Elija la plantilla de proyecto de C# que se denomina simplemente Biblioteca de clases (sin prefijos ni sufijos) y haga clic en Siguiente.
    3. Asigne al nuevo proyecto el nombre SimpleMathProjection. La ubicación ya debe establecerse en la misma \CsWinRT\src\Samples\NetProjectionSample carpeta en la que se encuentra la carpeta SimpleMathComponent , pero confirme eso. A continuación, haga clic en Siguiente.
    4. En la página Información adicional, seleccione .NET 6.0 (soporte a largo plazo), y a continuación, elija Crear.
  2. Elimine el archivo stub Class1.cs del proyecto.

  3. Siga estos pasos para instalar el paquete NuGet de C#/WinRT.

    1. En Explorador de Soluciones, haga clic con el botón derecho en su proyecto SimpleMathProjection y seleccione Administrar paquetes NuGet.
    2. En la pestaña Examinar, escriba o pegue Microsoft.Windows.CsWinRT en el cuadro de búsqueda, en los resultados de la búsqueda, seleccione el elemento con la versión más reciente y luego haga clic en Instalar para instalar el paquete en el proyecto SimpleMathProjection.
  4. Agregue a SimpleMathProjection una referencia de proyecto al proyecto SimpleMathComponent . En explorador de soluciones, haga clic con el botón derecho en el nodo Dependencias de en el nodo de proyecto SimpleMathProjection, seleccione Agregar referencia de proyectoy seleccione el proyecto SimpleMathComponent Aceptar.

Aún no intente compilar el proyecto. Lo haremos en un paso posterior.

Hasta ahora, el del Explorador de soluciones de debería ser similar a este (los números de versión serán diferentes).

Explorador de soluciones que muestra las dependencias del proyecto de proyección

Compilación de proyectos desde el código fuente

Para la solución de CppWinRTComponentProjectionSample en la de ejemplo de proyección de C#/WinRT (que descargó o clone de GitHub y ahora tiene abierta), la ubicación de salida de compilación se configura con el archivo Directory.Build.props para compilar fuera del origen. Esto significa que los archivos de la salida de compilación se generan fuera de la carpeta de origen. Recomendamos que compile fuera del directorio fuente al usar la herramienta C#/WinRT. Esto impide que el compilador de C# recopile accidentalmente todos los archivos *.cs en el directorio raíz del proyecto, lo que puede provocar errores de tipo duplicados (por ejemplo, al compilar para varias configuraciones o plataformas).

Aunque esto ya está configurado para la solución CppWinRTComponentProjectionSample, siga los pasos que se indican a continuación para practicar la configuración usted mismo.

Para configurar la solución para que se compile fuera del origen:

  1. Con la solución CppWinRTComponentProjectionSample todavía abierta, haga clic con el botón derecho sobre el nodo de la solución y seleccione AgregarNuevo elemento. Seleccione el archivo XML, y asígnelo el nombre Directory.Build.props (sin una extensión .xml). Haga clic en para sobrescribir el archivo existente.

  2. Reemplace el contenido de Directory.Build.props por la configuración siguiente.

    <Project>
      <PropertyGroup>
        <BuildOutDir>$([MSBuild]::NormalizeDirectory('$(SolutionDir)', '_build', '$(Platform)', '$(Configuration)'))</BuildOutDir>
        <OutDir>$([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'bin'))</OutDir>
        <IntDir>$([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'obj'))</IntDir>
      </PropertyGroup>
    </Project>
    
  3. Guarde y cierre el archivo Directory.Build.props .

Editar el archivo de proyecto para ejecutar C#/WinRT

Para poder invocar la cswinrt.exe herramienta para generar el ensamblado de proyección, primero debe editar el archivo de proyecto para especificar algunas propiedades del proyecto.

  1. En el Explorador de soluciones, haga doble clic en el nodo SimpleMathProjection para abrir el archivo de proyecto en el editor.

  2. Actualice el TargetFramework elemento para tener como destino una versión específica de Windows SDK. Esto agrega las dependencias de ensamblaje necesarias para la compatibilidad con la interoperabilidad y el soporte de proyección. Este ejemplo tiene como destino la versión de Windows SDK net6.0-windows10.0.19041.0 (también conocida como Windows 10, versión 2004). Establezca el Platform elemento en AnyCPU para que se pueda hacer referencia al ensamblado de proyección resultante desde cualquier arquitectura de aplicación. Para permitir que las aplicaciones de referencia admitan versiones anteriores de Windows SDK, también puede establecer la TargetPlatformMinimumVersion propiedad .

    <PropertyGroup>
      <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
      <!-- Set Platform to AnyCPU to allow consumption of the projection assembly from any architecture. -->
      <Platform>AnyCPU</Platform>
    </PropertyGroup>
    

    Nota:

    Para este tutorial y el código de ejemplo relacionado, la solución se compila para x64 y Release. Tenga en cuenta que el proyecto SimpleMathProjection está configurado para compilar para AnyCPU para todas las configuraciones de arquitectura de soluciones.

  3. Agregue un segundo PropertyGroup elemento (inmediatamente después del primero) que establezca varias propiedades de C#/WinRT.

    <PropertyGroup>
      <CsWinRTIncludes>SimpleMathComponent</CsWinRTIncludes>
      <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
    </PropertyGroup>
    

    Estos son algunos detalles sobre la configuración de este ejemplo:

    • La propiedad CsWinRTIncludes especifica los espacios de nombres que se van a proyectar.
    • La CsWinRTGeneratedFilesDir propiedad establece el directorio de salida en el que se generan los archivos de origen de proyección. Esta propiedad se establece en OutDir y se define en Directory.Build.props desde la sección anterior.
  4. Guarde y cierre el archivo SimpleMathProjection.csproj y haga clic en volver a cargar proyectos si es necesario.

Crear un paquete NuGet con la proyección

Para distribuir el ensamblado de proyección para desarrolladores de aplicaciones .NET, puede crear automáticamente un paquete NuGet al compilar la solución agregando algunas propiedades de proyecto adicionales. Para los destinos de .NET, el paquete NuGet debe incluir el ensamblado de proyección y el ensamblado de implementación del componente.

  1. Siga estos pasos para agregar un archivo de especificación de NuGet (.nuspec) al proyecto SimpleMathProjection .

    1. En Explorador de soluciones, haga clic con el botón derecho en el nodo SimpleMathProjection, elija Agregar>Nueva Carpetay asigne el nombre nugeta la carpeta.
    2. Haga clic con el botón derecho en la carpeta nuget , elija Agregar>nuevo elemento, elija archivo XML y asígnele el nombre SimpleMathProjection.nuspec.
  2. En el Explorador de soluciones, haga doble clic en el nodo SimpleMathProjection para abrir el archivo de proyecto en el editor. Agregue el siguiente grupo de propiedades a SimpleMathProjection.csproj ahora abierto (inmediatamente después de los dos elementos existentes PropertyGroup ) para generar automáticamente el paquete. Estas propiedades especifican el NuspecFile y el directorio para generar el paquete NuGet.

    <PropertyGroup>
      <GeneratedNugetDir>.\nuget\</GeneratedNugetDir>
      <NuspecFile>$(GeneratedNugetDir)SimpleMathProjection.nuspec</NuspecFile>
      <OutputPath>$(GeneratedNugetDir)</OutputPath>
      <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    </PropertyGroup>
    

    Nota:

    Si prefiere generar un paquete por separado, también puede elegir ejecutar la nuget.exe herramienta desde la línea de comandos. Para obtener más información sobre cómo crear un paquete NuGet, consulte Creación de un paquete mediante la CLI de nuget.exe.

  3. Abra el archivo SimpleMathProjection.nuspec para editar las propiedades de creación del paquete y pegue el código siguiente. El fragmento de código siguiente es un ejemplo de especificación de NuGet para distribuir SimpleMathComponent a varios marcos de destino. Tenga en cuenta que el ensamblado de proyección, SimpleMathProjection.dll, se especifica en lugar de SimpleMathComponent.winmd para el objetivo lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll. Este comportamiento es nuevo en .NET 6 y versiones posteriores y está habilitado por C#/WinRT. El ensamblado de implementación, SimpleMathComponent.dll, también debe distribuirse y se cargará en tiempo de ejecución.

    <?xml version="1.0" encoding="utf-8"?>
    <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
      <metadata>
        <id>SimpleMathComponent</id>
        <version>0.1.0-prerelease</version>
        <authors>Contoso Math Inc.</authors>
        <description>A simple component with basic math operations</description>
        <dependencies>
          <group targetFramework="net6.0-windows10.0.19041.0" />
          <group targetFramework=".NETCoreApp3.0" />
          <group targetFramework="UAP10.0" />
          <group targetFramework=".NETFramework4.6" />
        </dependencies>
      </metadata>
      <files>
        <!--Support .NET 6, .NET Core 3, UAP, .NET Framework 4.6, C++ -->
        <!--Architecture-neutral assemblies-->
        <file src="..\..\_build\AnyCPU\Release\SimpleMathProjection\bin\SimpleMathProjection.dll" target="lib\net6.0-windows10.0.19041.0\SimpleMathProjection.dll" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\netcoreapp3.0\SimpleMathComponent.winmd" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\uap10.0\SimpleMathComponent.winmd" />
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.winmd" target="lib\net46\SimpleMathComponent.winmd" />
        <!--Architecture-specific implementation DLLs should be copied into RID-relative folders-->
        <file src="..\..\_build\x64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-x64\native\SimpleMathComponent.dll" />
        <!--To support x86 and Arm64, build SimpleMathComponent for those other architectures and uncomment the entries below.-->
        <!--<file src="..\..\_build\Win32\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-x86\native\SimpleMathComponent.dll" />-->
        <!--<file src="..\..\_build\arm64\Release\SimpleMathComponent\bin\SimpleMathComponent\SimpleMathComponent.dll" target="runtimes\win10-arm64\native\SimpleMathComponent.dll" />-->
      </files>
    </package>
    

    Nota:

    SimpleMathComponent.dll, el ensamblado de implementación del componente es específico de la arquitectura. Si admite otras plataformas (por ejemplo, x86 o Arm64), primero debe compilar SimpleMathComponent para las plataformas deseadas y agregar estos archivos de ensamblado a la carpeta relativa a RID adecuada. El ensamblado de proyección SimpleMathProjection.dll y el componente SimpleMathComponent.winmd son ambos independientes de la arquitectura.

  4. Guarde y cierre los archivos que acaba de editar.

Compilación de la solución para generar la proyección y el paquete NuGet

Antes de compilar la solución, asegúrese de comprobar la configuración de de Configuration Manager en Visual Studio, en BuildConfiguration Manager. Para este tutorial, establezca la configuración de en release y Platform en x64 para la solución.

En este momento, ahora puede compilar la solución. Haga clic con el botón derecho en el nodo de la solución y seleccione Compilar la solución. Esto compilará primero el proyecto SimpleMathComponent, y a continuación, el proyecto SimpleMathProjection. El ensamblado de implementación y WinMD del componente (SimpleMathComponent.winmd y SimpleMathComponent.dll), los archivos de origen de proyección y el ensamblado de proyección (SimpleMathProjection.dll), se generarán todos en el directorio de salida de _build. También podrá ver el paquete NuGet generado, SimpleMathComponent0.1.0-prerelease.nupkg, en la carpeta \SimpleMathProjection\nuget .

Importante

Si no se genera alguno de los archivos mencionados anteriormente, compile la solución una segunda vez. También es posible que tenga que cerrar y volver a abrir la solución antes de reconstruirla.

Es posible que tenga que cerrar y volver a abrir la solución para que el .nupkg aparezca en Visual Studio como se muestra (o simplemente seleccione y, a continuación, anule la selección de Mostrar todos los archivos).

Explorador de soluciones mostrando generación de proyección

Referencia al paquete NuGet en una aplicación de consola de .NET 6 de C#

Para consumir SimpleMathComponent desde un proyecto de .NET, simplemente puede agregar una referencia al paquete NuGet SimpleMathComponent0.1.0-prerelease.nupkg a un nuevo proyecto de .NET que creamos en la sección anterior. En los pasos siguientes se muestra cómo hacerlo mediante la creación de una aplicación de consola sencilla en una solución independiente.

  1. Siga estos pasos para crear una nueva solución que contenga un proyecto de aplicación de consola C# (crear este proyecto en una nueva solución le permite restaurar independientemente el paquete NuGet SimpleMathComponent).

    Importante

    Vamos a crear este nuevo proyecto de aplicación de consola dentro de la carpeta \CsWinRT\src\Samples\NetProjectionSample, que encontrará en su descarga o clon del ejemplo de proyección de C#/WinRT .

    1. En una nueva instancia de Visual Studio, seleccione Archivo>Nuevo>Proyecto.
    2. En el cuadro de diálogo Crear un nuevo proyecto, busque la plantilla de proyecto aplicación de consola. Elija la plantilla de proyecto de C# simplemente llamada Aplicación de Consola (sin prefijos ni sufijos), y haga clic en Siguiente. Si está usando Visual Studio 2019, la plantilla de proyecto es Aplicación de Consola.
    3. Asigne al nuevo proyecto el nombre SampleConsoleApp, establezca su ubicación en la misma carpeta \CsWinRT\src\Samples\NetProjectionSample en la que se encuentran las carpetas SimpleMathComponent y SimpleMathProjection y haga clic en Siguiente.
    4. En la página Información adicional, seleccione .NET 6.0 (soporte a largo plazo), y a continuación, elija Crear.
  2. En Solution Explorer, haga doble clic en el nodo SampleConsoleApp para abrir el archivo de proyecto SampleConsoleApp.csproj y edite las propiedades TargetFramework y Platform para que queden como se muestran en la lista siguiente. Agregue el Platform elemento si no está ahí.

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
      <Platform>x64</Platform>
    </PropertyGroup>
    
  3. Con el archivo de proyecto SampleConsoleApp.csproj todavía abierto, añadiremos en el proyecto SampleConsoleApp una referencia al paquete SimpleMathComponent NuGet. Para restaurar el SimpleMathComponent NuGet al compilar el proyecto, puede usar la propiedad con la ruta de acceso a la carpeta nuget de la solución de componentes. Copie la siguiente configuración y péguela en SampleConsoleApp.csproj (dentro del Project elemento).

    <PropertyGroup>
      <RestoreSources>
        https://api.nuget.org/v3/index.json;
        ../SimpleMathProjection/nuget
      </RestoreSources>
    </PropertyGroup>
    
    <ItemGroup>
      <PackageReference Include="SimpleMathComponent" Version="0.1.0-prerelease" />
    </ItemGroup>
    

    Importante

    La ruta de acceso RestoreSources para el paquete SimpleMathComponent se establece en ../SimpleMathProjection/nuget. Esa ruta de acceso es correcta siempre que haya seguido los pasos descritos en este tutorial, para que los proyectos SimpleMathComponent y SampleConsoleApp se encuentren en la misma carpeta (la carpeta NetProjectionSample, en este caso). Si has hecho algo diferente, tendrás que ajustar esa ruta de acceso en consecuencia. Como alternativa, puede agregar una fuente de paquetes NuGet local a la solución.

  4. Edite el archivo Program.cs para usar la funcionalidad proporcionada por SimpleMathComponent.

    var x = new SimpleMathComponent.SimpleMath();
    Console.WriteLine("Adding 5.5 + 6.5 ...");
    Console.WriteLine(x.add(5.5, 6.5).ToString());
    
  5. Guarde y cierre los archivos que acaba de editar y compile y ejecute la aplicación de consola. Deberías ver el resultado a continuación.

    salida de la consola NET5

Problemas conocidos

  • Al compilar el proyecto de proyección, podría aparecer un error como: Error MSB3271 Hubo una discrepancia entre la arquitectura del procesador del proyecto que se está compilando "MSIL" y la arquitectura del procesador "x86" del archivo de implementación "..\SimpleMathComponent.dll" para "..\SimpleMathComponent.winmd". Esta falta de coincidencia puede provocar fallos en tiempo de ejecución. Considere cambiar la arquitectura de procesador objetivo de su proyecto mediante el Configuration Manager para alinear las arquitecturas de procesador entre su proyecto y el archivo de implementación, o elija un archivo winmd con un archivo de implementación cuya arquitectura de procesador coincida con la arquitectura de procesador objetivo de su proyecto. Para solucionar este error, agregue la siguiente propiedad al archivo de proyecto de su biblioteca C#:
    <PropertyGroup>
        <!-- Workaround for MSB3271 error on processor architecture mismatch -->
        <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
    </PropertyGroup>
    

Consideraciones adicionales

El ensamblado de proyección (o interoperabilidad) de C# que mostramos cómo crear en este tema es bastante sencillo, no tiene dependencias en otros componentes. Pero para generar una proyección de C# para un componente de C++/WinRT que tenga referencias a los tipos de Windows App SDK, en el proyecto de proyección deberá agregar una referencia al paquete NuGet del SDK de aplicaciones de Windows. Si faltan estas referencias, verá errores como "No se encontró el tipo <T> ".

Otra cosa que hacemos en este tema es distribuir la proyección como un paquete NuGet. Ese es necesario actualmente.

Recursos