Creación de un componente de Windows Runtime con C# para usar desde una aplicación de C++/WinRT

Este tema le guía por el proceso de agregar un componente sencillo de C# a un proyecto de C++/WinRT.

Visual Studio facilita el proceso de crear e implementar sus propios tipos de Windows Runtime personalizados en un proyecto de componente de Windows Runtime (WRC) escrito con C# o Visual Basic y, a continuación, hacer referencia a ese WRC desde un proyecto de aplicación de C++ y consumir esos tipos personalizados desde esa aplicación.

Internamente, los tipos de Windows Runtime pueden usar cualquier funcionalidad de .NET permitida en una aplicación para UWP.

Externamente, los miembros de los tipos pueden exponer solo los tipos de Windows Runtime para sus parámetros y valores devueltos. Cuando se compila una solución, Visual Studio compila el proyecto de WRC de .NET y, después, ejecuta un paso de compilación que crea un archivo de metadatos de Windows (.winmd). Este es el componente de Windows Runtime (WRC) que Visual Studio incluye en su aplicación.

Nota

.NET asigna automáticamente algunos de los tipos de .NET más usados (como tipos de datos primitivos y tipos de colección) a sus equivalentes de Windows Runtime. Estos tipos de .NET pueden usarse en la interfaz pública de un componente de Windows Runtime y se mostrarán a los usuarios del componente como los tipos correspondientes de Windows Runtime. Consulte Componentes de Windows Runtime con C# y Visual Basic.

Prerrequisitos

Creación de una aplicación en blanco

En Visual Studio, crea un nuevo proyecto con la plantilla de proyecto Aplicación en blanco (C++/WinRT) . Asegúrate de que usas la plantilla de (C++/WinRT) , y no la de (Windows universal) .

Establezca el nombre del nuevo proyecto en CppToCSharpWinRT para que la estructura de carpetas coincida con la del tutorial.

Adición de un componente de Windows Runtime de C# a la solución

En Visual Studio, cree el proyecto del componente. Para ello, vaya al Explorador de soluciones, abra el menú contextual de la solución CppToCSharpWinRT y elija Agregar. A continuación, elija Nuevo proyecto para agregar un nuevo proyecto de C# a la solución. En la sección Plantillas instaladas del cuadro de diálogo Agregar nuevo proyecto, seleccione Visual C# y, a continuación, elija Windows y, después, Universal. Seleccione la plantilla Componente de Windows Runtime (Windows universal) y escriba SampleComponent en el nombre del proyecto.

Nota

En el cuadro de diálogo Nuevo proyecto de la Plataforma universal de Windows, elija Windows 10 Creators Update (10.0; compilación 15063) como versión mínima. Para obtener más información, consulte la sección Versión mínima de la aplicación a continuación.

Adición del método GetMyString de C#

En el proyecto SampleComponent, cambie el nombre de la clase de Class1 a Example. A continuación, agregue dos miembros simples a la clase, un campo int privado y un método de instancia denominado GetMyString:

    public sealed class Example
    {
        int MyNumber;

        public string GetMyString()
        {
            return $"This is call #: {++MyNumber}";
        }
    }

Nota

De manera predeterminada, la clase se marca como public sealed. Todas las clases de Windows Runtime que exponga desde el componente deben ser sealed.

Nota

Opcional: para habilitar IntelliSense en los miembros recién agregados, en el Explorador de soluciones, abra el menú contextual del proyecto SampleComponent y, a continuación, elija Compilación.

Referencia a SampleComponent de C# desde el proyecto CppToCSharpWinRT

En el Explorador de soluciones, en el proyecto de C++/WinRT, abra el menú contextual de Referencias y, a continuación, elija Agregar referencia para abrir el cuadro de diálogo Agregar referencia. Elige Proyectos y, a continuación, Solución. Seleccione la casilla de verificación del proyecto SampleComponent y elija Aceptar para agregar una referencia.

Nota

Opcional: para habilitar IntelliSense en el proyecto de C++/WinRT, en el Explorador de soluciones, abra el menú contextual del proyecto CppToCSharpWinRT y, a continuación, elija Compilación.

Edición de MainPage.h

Abra MainPage.h en el proyecto CppToCSharpWinRT y, a continuación, agregue dos elementos. En primer lugar, agregue #include "winrt/SampleComponent.h" al final de las instrucciones #include y, a continuación, un campo winrt::SampleComponent::Example al struct MainPage.

// MainPage.h
...
#include "winrt/SampleComponent.h"

namespace winrt::CppToCSharpWinRT::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
...
        winrt::SampleComponent::Example myExample;
...
    };
}

Nota

En Visual Studio, MainPage.h se muestra en MainPage.xaml.

Editar MainPage.cpp

En MainPage.cpp, cambie la implementación Mainpage::ClickHandler para llamar al método GetMyString de C#.

void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
{
    //myButton().Content(box_value(L"Clicked"));

    hstring myString = myExample.GetMyString();

    myButton().Content(box_value(myString));
}

Ejecución del proyecto

Ahora puedes compilar y ejecutar el proyecto. Cada vez que haga clic en el botón, se incrementará el número en el botón.

C++/WinRT Windows calling into a C# component screenshot

Sugerencia

En Visual Studio, cree el proyecto de componente: en el Explorador de soluciones, abra el menú contextual del proyecto CppToCSharpWinRT, elija Propiedades y, a continuación, elija Depuración en Propiedades de configuración. Establezca el tipo de depurador en Administrado y nativo si quiere depurar el código tanto de C# (administrado) como de C++ (nativo). C++ Debugging Properties

Versión mínima de la aplicación

La versión mínima de la aplicación del proyecto de C# controlará la versión de .NET usada para compilar la aplicación. Por ejemplo, si elige Windows 10 Fall Creators Update (10.0; compilación 16299) o posterior, se habilitará la compatibilidad con .NET Standard 2.0 y el procesador Arm64 de Windows.

Sugerencia

Se recomienda usar versiones mínimas de aplicación inferiores a 16299 para evitar la configuración de compilación adicional si no es necesaria la compatibilidad con .NET Standard 2.0 o con ARM64.

Configuración para Windows 10 Fall Creators Update (10.0; compilación 16299)

Siga estos pasos para habilitar la compatibilidad con .NET Standard 2.0 o ARM64 de Windows en los proyectos de C# a los que se hace referencia desde el proyecto de C++/WinRT.

En Visual Studio, vaya al Explorador de soluciones y abra el menú contextual del proyecto CppToCSharpWinRT. Elija Propiedades y establezca la versión mínima de la aplicación universal de Windows en Windows 10 Fall Creators Update (10.0; compilación 16299) (o superior). Haga lo mismo para el proyecto SampleComponent.

En Visual Studio, abra el menú contextual del proyecto CppToCSharpWinRT y elija Descargar el proyecto para abrir CppToCSharpWinRT.vcxproj en el editor de texto.

Copie el siguiente código XML y péguelo en el primer PropertyGroup de CPPWinRTCSharpV2.vcxproj.

   <!-- Start Custom .NET Native properties -->
   <DotNetNativeVersion>2.2.12-rel-31116-00</DotNetNativeVersion>
   <DotNetNativeSharedLibrary>2.2.8-rel-31116-00</DotNetNativeSharedLibrary>
   <UWPCoreRuntimeSdkVersion>2.2.14</UWPCoreRuntimeSdkVersion>
   <!--<NugetPath>$(USERPROFILE)\.nuget\packages</NugetPath>-->
   <NugetPath>$(ProgramFiles)\Microsoft SDKs\UWPNuGetPackages</NugetPath>
   <!-- End Custom .NET Native properties -->

Los valores de DotNetNativeVersion, DotNetNativeSharedLibrary y UWPCoreRuntimeSdkVersion pueden variar según la versión de Visual Studio. Para establecerlos en los valores correctos, abra %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages y examine el subdirectorio para cada valor de la tabla siguiente. El directorio %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\Microsoft.Net.Native.Compiler tendrá un subgrupo que contiene una versión instalada de .NET nativo que comienza por 2.2. En el ejemplo que aparece a continuación es 2.2.12-rel-31116-00.

Variable de MSBuild Directorio Ejemplo
DotNetNativeVersion %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\Microsoft.Net.Native.Compiler 2.2.12-rel-31116-00
DotNetNativeSharedLibrary %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\runtime.win10-x64.microsoft.net.native.sharedlibrary 2.2.8-rel-31116-00
UWPCoreRuntimeSdkVersion %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\Microsoft.Net.UWPCoreRuntimeSdk 2.2.14

Nota

Hay varias arquitecturas admitidas para Microsoft.Net.Native.SharedLibrary. Reemplace x64 por la arquitectura adecuada. Por ejemplo, la arquitectura arm64 estaría en el directorio %ProgramFiles(x86)%\Microsoft SDKs\UWPNuGetPackages\runtime.win10-arm64.microsoft.net.native.sharedlibrary.

A continuación, inmediatamente después del primer elemento PropertyGroup, agregue lo siguiente (sin modificarlo).

  <!-- Start Custom .NET Native targets -->
  <!-- Import all of the .NET Native / CoreCLR props at the beginning of the project -->
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\Microsoft.Net.UWPCoreRuntimeSdk.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-x86.Microsoft.Net.UWPCoreRuntimeSdk.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-x64.Microsoft.Net.UWPCoreRuntimeSdk.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-arm.Microsoft.Net.UWPCoreRuntimeSdk.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-x86.Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-x64.Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-arm.Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm64.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-arm64.Microsoft.Net.Native.Compiler.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-x86.Microsoft.Net.Native.SharedLibrary.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-x64.Microsoft.Net.Native.SharedLibrary.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-arm.Microsoft.Net.Native.SharedLibrary.props" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm64.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-arm64.Microsoft.Net.Native.SharedLibrary.props" />
  <!-- End Custom .NET Native targets -->

Al final del archivo de proyecto, justo antes de la etiqueta de cierre Project, agregue lo siguiente (sin modificarlo).

  <!-- Import all of the .NET Native / CoreCLR targets at the end of the project -->
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-x86.Microsoft.Net.UWPCoreRuntimeSdk.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-x64.Microsoft.Net.UWPCoreRuntimeSdk.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.UWPCoreRuntimeSdk\$(UWPCoreRuntimeSdkVersion)\build\runtime.win10-arm.Microsoft.Net.UWPCoreRuntimeSdk.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-x86.Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-x64.Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-arm.Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm64.Microsoft.Net.Native.Compiler\$(DotNetNativeVersion)\build\runtime.win10-arm64.Microsoft.Net.Native.Compiler.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x86.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-x86.Microsoft.Net.Native.SharedLibrary.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-x64.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-x64.Microsoft.Net.Native.SharedLibrary.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-arm.Microsoft.Net.Native.SharedLibrary.targets" />
  <Import Condition="'$(WindowsTargetPlatformMinVersion)' &gt;= '10.0.16299.0'" Project="$(NugetPath)\runtime.win10-arm64.Microsoft.Net.Native.SharedLibrary\$(DotNetNativeSharedLibrary)\build\runtime.win10-arm64.Microsoft.Net.Native.SharedLibrary.targets" />
  <!-- End Custom .NET Native targets -->

Vuelva a cargar el archivo de proyecto en Visual Studio. Para ello, en el Explorador de soluciones de Visual Studio, abra el menú contextual del proyecto CppToCSharpWinRT y elija Volver a cargar el proyecto.

Compilación para .NET Native

Se recomienda compilar y probar la aplicación con el componente de C# creado en .NET Native. En Visual Studio, abra el menú contextual del proyecto CppToCSharpWinRT y elija Descargar el proyecto para abrir CppToCSharpWinRT.vcxproj en el editor de texto.

A continuación, establezca la propiedad UseDotNetNativeToolchain en true en las configuraciones de Release y ARM64 del archivo de proyecto de C++.

En el Explorador de soluciones de Visual Studio, abra el menú contextual del proyecto CppToCSharpWinRT y elija Volver a cargar el proyecto.

  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
...
    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Platform)'=='Arm64'" Label="Configuration">
    <UseDotNetNativeToolchain Condition="'$(UseDotNetNativeToolchain)'==''">true</UseDotNetNativeToolchain>
  </PropertyGroup>

Referencia a otros paquetes NuGet de C#

Si el componente de C# hace referencia a otros paquetes NuGet, es posible que el archivo de proyecto de la aplicación necesite enumerar las dependencias del archivo del paquete NuGet como contenido de implementación. Por ejemplo, si el componente de C# hace referencia al paquete NuGet Newtonsoft.Json, también se debe hacer referencia al mismo paquete NuGet y dependencia de archivo en el proyecto de aplicación.

En el archivo SampleComponent.csproj, agregue la referencia del paquete NuGet:

    <PackageReference Include="Newtonsoft.Json">
      <Version>13.0.1</Version>
    </PackageReference>

En el proyecto CppToCSharpWinRT, busque el archivo packages.config y agregue la referencia de NuGet adecuada. Esto instalará el paquete NuGet en la carpeta del paquete de la solución.

En packages.config, agregue la misma referencia de paquete NuGet:

  <package id="Newtonsoft.Json" version="13.0.1" targetFramework="native" developmentDependency="true" />

A continuación, agregue lo siguiente al archivo de proyecto de la aplicación para hacer referencia a la dependencia de archivo adecuada desde la carpeta de paquetes de la solución. Por ejemplo, en CppToCSharpWinRT.vcxproj, agregue lo siguiente:

  <ItemGroup>
    <None Include="..\packages\Newtonsoft.Json.13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll">
      <Link>%(Filename)%(Extension)</Link>
      <DeploymentContent>true</DeploymentContent>
    </None>
  </ItemGroup>