Llamada a las a API de Windows Runtime en aplicaciones de escritorio

En este artículo se describe cómo configurar los proyectos de aplicación de escritorio para usar las API de Windows Runtime (WinRT) que proporciona el sistema operativo Windows y agregar experiencias modernas de Windows 11 y Windows 10 a las aplicaciones de escritorio.

Algunas API de Windows Runtime (WinRT) no se admiten en aplicaciones de escritorio. Para obtener más información, consulte API de Windows Runtime disponibles para aplicaciones de escritorio.

Modificación de un proyecto de .NET para usar las API de Windows Runtime

Hay varias opciones para los proyectos de .NET:

  • A partir de .NET 6, puede especificar un moniker de la plataforma de destino (TFM) en el archivo de proyecto para acceder a las API de WinRT. Esta opción se admite en proyectos que tienen como destino Windows 10, versión 1809 o posterior.
  • Para versiones anteriores de .NET, puede instalar el paquete NuGet Microsoft.Windows.SDK.Contracts para agregar todas las referencias necesarias al proyecto. Esta opción se admite en proyectos que tienen como destino Windows 10, versión 1803 o posterior.
  • Si el proyecto tiene varios destinos .NET 6 (o posterior) y versiones anteriores de .NET, puede configurar el archivo del proyecto para usar ambas opciones.

.NET 6 y versiones posteriores: uso de la opción de moniker de la plataforma de destino

Esta opción solo se admite en proyectos que usan .NET 6 (o una versión posterior), y están destinados a Windows 10, versión 1809 o una versión posterior del sistema operativo. Al especificar un TFM específico de la versión del sistema operativo Windows en el archivo del proyecto, se agrega una referencia al paquete de destino del SDK de Windows adecuado. Para obtener más información general sobre este escenario, consulte la entrada de blog Llamada de API de Windows en .NET.

  1. Con el proyecto abierto en Visual Studio, haga clic con el botón derecho en el proyecto en Explorador de soluciones y elija Editar archivo de proyecto. El archivo de proyecto debería tener un aspecto similar al siguiente.

    Nota

    En el ejemplo siguiente se muestra un elemento OutputTypeWinExe, que especifica un ejecutable de GUI de Windows (e impide que se abra una ventana de consola cuando se ejecuta la aplicación). Si la aplicación no tiene ninguna GUI, OutputType tendrá un valor diferente. Puede llamar a las API de WinRT desde aplicaciones de GUI de Windows, aplicaciones de consola y bibliotecas. Además, es posible que el valor de TargetFramework no coincida exactamente con el ejemplo siguiente.

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
      </PropertyGroup>
    </Project>
    
  2. Deje todos los demás valores tal como están y reemplace el valor del elemento TargetFramework por una de las cadenas siguientes:

    • net6.0-windows10.0.17763.0: si la aplicación tiene como destino Windows 10, versión 1809.
    • net6.0-windows10.0.18362.0: si la aplicación tiene como destino Windows 10, versión 1903.
    • net6.0-windows10.0.19041.0: si la aplicación tiene como destino Windows 10, versión 2004.
    • net6.0-windows10.0.22000.0: si la aplicación tiene como destino Windows 11.

    Por ejemplo, el siguiente elemento es para un proyecto que tiene como destino Windows 10, versión 2004.

    <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
    

    En versiones posteriores de .NET, puede reemplazar el valor por la versión correspondiente, por ejemplo net6.0-windows10.0.19041.0.

  3. Guarde los cambios y cierre el archivo de proyecto.

Las API de WinRT no se admiten en .NET 6 ni versiones posteriores

En .NET 6 y versiones posteriores, hay varias API de Windows Runtime (WinRT) en el espacio de nombres Windows.UI que no se admiten. Para las API que se enumeran a continuación, existen versiones de API equivalentes en el espacio de nombres WinUI (Microsoft.UI) (por ejemplo, Microsoft.UI.Text). Las siguientes API de WinRT no se admiten en .NET 6 y versiones posteriores:

Compatibilidad con varias versiones del sistema operativo Windows

La propiedad TargetFramework específica de la versión del sistema operativo Windows determina la versión de Windows SDK con la que se compila la aplicación. Esta propiedad determina el conjunto de API accesibles en tiempo de compilación y proporciona valores predeterminados para TargetPlatformVersion y TargetPlatformMinVersion (si no se establece explícitamente). No es necesario definir explícitamente la propiedad TargetPlatformVersion en el archivo de proyecto, ya que la establece automáticamente la versión del sistema operativo de TargetFramework.

TargetPlatformMinVersion se puede invalidar para que sea menor que TargetPlatformVersion (determinado por la versión de la propiedad TargetFramework). Esto permite que una aplicación se ejecute en versiones anteriores del sistema operativo. Por ejemplo, puede establecer lo siguiente en el archivo de proyecto para admitir la aplicación de nivel inferior en Windows 10, versión 1809.

<Project Sdk="Microsoft.NET.Sdk">
 <PropertyGroup>
   <OutputType>WinExe</OutputType>
   <TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
   <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
 </PropertyGroup>
</Project>

Tenga en cuenta que establecer TargetPlatformMinVersion en una versión inferior a TargetPlatformVersion crea la posibilidad de llamar a API no disponibles. Al llamar a API de WinRT que no están disponibles en todas las versiones de sistema operativo admitidas, se recomienda proteger estas llamadas con comprobaciones ApiInformation. Para más información, consulte Aplicaciones adaptables para versiones.

Versiones anteriores de .NET: Instale el paquete NuGet Microsoft.Windows.SDK.Contracts.

Use esta opción si la aplicación usa .NET Core 3.x o .NET Framework. Esta opción se admite en proyectos que tienen como destino Windows 10, versión 1803 o posterior.

  1. Asegúrate de que las referencias de paquete están habilitadas:

    1. En Visual Studio, haga clic en Herramientas-> Administrador de paquetes NuGet-> Configuración del Administrador de paquetes.
    2. Asegúrate de que PackageReference está seleccionado para Formato predeterminado de administración de paquetes.
  2. Una vez hayas creado tu proyecto en Visual Studio, haz clic con el botón derecho en el Explorador de soluciones y elige Administrar paquetes NuGet.

  3. En la ventana Administrador de paquetes NuGet, selecciona la pestaña Examinar y busca Microsoft.Windows.SDK.Contracts.

  4. Una vez encontrado el paquete Microsoft.Windows.SDK.Contracts, en el panel derecho de la ventana Administrador de paquetes NuGet, selecciona la Versión del paquete que quieres instalar en función de la versión de Windows 10 de destino deseada:

    • 10.0.19041.xxxx: elija esta versión para Windows 10, versión 2004.
    • 10.0.18362.xxxx: elije esta versión para Windows 10, versión 1903.
    • 10.0.17763.xxxx: elije esta versión para Windows 10, versión 1809.
    • 10.0.17134.xxxx: elije esta versión para Windows 10, versión 1803.
  5. Haga clic en Instalar.

Configuración de proyectos que tienen como destino diferentes versiones de .NET

Si el proyecto tiene varios destinos de .NET 6 (o posterior) y versiones anteriores (incluidos .NET Core 3.x y .NET Framework), puede configurar el archivo del proyecto para que use el moniker de la plataforma de destino (TFM) a fin de extraer automáticamente las referencias de API de WinRT para .NET 6 y usar el paquete NuGet Microsoft.Windows.SDK.Contracts para versiones anteriores.

  1. Con el proyecto abierto en Visual Studio, haga clic con el botón derecho en el proyecto en Explorador de soluciones y elija Editar archivo de proyecto. En el ejemplo siguiente se muestra un archivo de proyecto para una aplicación que usa .NET Core 3.1.

    Nota

    En el ejemplo siguiente se muestra un elemento OutputTypeWinExe, que especifica un ejecutable de GUI de Windows (e impide que se abra una ventana de consola cuando se ejecuta la aplicación). Si la aplicación no tiene ninguna GUI, OutputType tendrá un valor diferente. Puede llamar a las API de WinRT desde aplicaciones de GUI de Windows, aplicaciones de consola y bibliotecas. Además, es posible que el valor de TargetFramework no coincida exactamente con el ejemplo siguiente.

    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
        <UseWindowsForms>true</UseWindowsForms>
      </PropertyGroup>
    </Project>
    
  2. Reemplace el elemento TargetFramework del archivo por un elemento TargetFrameworks (tenga en cuenta el plural). En este elemento, especifique los monikers de la plataforma de destino (TFM) para todas las versiones de .NET que quiera establecer como destino, separadas por punto y coma.

    • Para .NET 6 o versiones posteriores, use uno de los siguientes monikers de la plataforma de destino (TFM):
      • net6.0-windows10.0.17763.0: si la aplicación tiene como destino Windows 10, versión 1809.
      • net6.0-windows10.0.18362.0: si la aplicación tiene como destino Windows 10, versión 1903.
      • net6.0-windows10.0.19041.0: si la aplicación tiene como destino Windows 10, versión 2004.
    • Para .NET Core 3.x, use netcoreapp3.0 o netcoreapp3.1.
    • Para .NET Framework, use net46.

    En el ejemplo siguiente se muestra cómo establecer varios destinos .NET Core 3.1 y .NET 6 (para Windows 10, versión 2004).

    <TargetFrameworks>netcoreapp3.1;net6.0-windows10.0.19041.0</TargetFrameworks>
    
  3. Después del elemento PropertyGroup, agregue un elemento PackageReference que incluya una instrucción condicional que instale el paquete NuGet Microsoft.Windows.SDK.Contracts para cualquier versión de .NET Core 3.x o .NET Framework que la aplicación tenga como destino. El elemento PackageReference debe ser un elemento secundario de un elemento ItemGroup. En el siguiente ejemplo, se muestra cómo hacerlo para .NET Core 3.1.

    <ItemGroup>
      <PackageReference Condition="'$(TargetFramework)' == 'netcoreapp3.1'"
                        Include="Microsoft.Windows.SDK.Contracts"
                        Version="10.0.19041.0" />
    </ItemGroup>
    

    Cuando haya terminado, el archivo de proyecto debería tener un aspecto similar al siguiente.

    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFrameworks>netcoreapp3.1;net6.0-windows10.0.19041.0</TargetFrameworks>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
      <ItemGroup>
        <PackageReference Condition="'$(TargetFramework)' == 'netcoreapp3.1'"
                         Include="Microsoft.Windows.SDK.Contracts"
                         Version="10.0.19041.0" />
      </ItemGroup>
    </Project>
    
  4. Guarde los cambios y cierre el archivo de proyecto.

Modificación de un proyecto de C++ de escritorio (Win32) para usar las API de Windows Runtime

Use C++/WinRT para utilizar las API de WinRT. C++/WinRT es una moderna proyección de lenguaje C++17 totalmente estándar para las API de WinRT, implementada como una biblioteca basada en archivos de encabezado y diseñada para proporcionarle acceso de primera clase a la API moderna de Windows.

Para configurar el proyecto para C++/WinRT:

Para más información sobre estás opciones, consulte Compatibilidad de Visual Studio con C++/WinRT, XAML, la extensión VSIX y el paquete NuGet.

Adición de experiencias de Windows 10

Ya estás listo para agregar experiencias modernas que dan a conocer cuándo los usuarios ejecutan tu aplicación en Windows 10. Usa este flujo de diseño.

En primer lugar, decida qué experiencias quiere agregar

Hay muchas entre las que elegir. Por ejemplo, puedes simplificar el flujo de pedidos de compra mediante las API de rentabilidad o dirigir la atención a tu aplicación cuando tengas algo interesante que compartir, por ejemplo, una nueva imagen que otro usuario ha publicado.

Toast notification

Aunque los usuarios ignoren o descarten el mensaje, pueden verlo de nuevo en el centro de actividades y, a continuación, hacer clic en el mensaje para abrir tu aplicación. Esto aumenta la relación con tu aplicación y tiene la ventaja adicional de que la aplicación parezca perfectamente integrada con el sistema operativo. Te mostraremos el código para esa experiencia más adelante en este artículo.

Consulta la documentación de UWP para obtener más ideas.

Decida si prefiere mejorar o ampliar

A menudo nos escucharás usar los términos mejorar y ampliar, por lo que vamos a detenernos un momento para explicar exactamente qué significan cada uno.

Usamos el término mejorar para describir las API de WinRT a las que puede llamar directamente desde la aplicación de escritorio, independientemente que sea, o no, una aplicación empaquetada. Cuando hayas elegido una experiencia de Windows 10, identifica las API que necesitas para crearla y comprueba si esa API aparece en esta lista. Esta es una lista de las API a las que puedes llamar directamente desde tu aplicación de escritorio. Si tu API no aparece en esta lista, se debe a que la funcionalidad asociada a esa API solo se puede ejecutar dentro de un proceso de UWP. A menudo, incluyen API que representan XAML de UWP, como un control de mapa de UWP o una advertencia de seguridad de Windows Hello.

Nota

Aunque normalmente no se puede llamar a las API que representan XAML de UWP directamente desde el escritorio, es posible que puedas usar enfoques alternativos. Si quieres hospedar controles XAML de UWP u otras experiencias visuales personalizadas, puedes usar islas XAML (a partir de Windows 10, versión 1903) y la capa visual (a partir de Windows 10, versión 1803). Estas características se pueden usar en aplicaciones de escritorio empaquetadas o sin empaquetar.

Si ha optado por empaquetar la aplicación de escritorio, otra opción es ampliar la aplicación agregando un proyecto de UWP a la solución. El proyecto de escritorio sigue siendo el punto de entrada de tu aplicación, pero el proyecto de UWP te proporciona acceso a todas las API que no aparecen en esta lista. La aplicación de escritorio puede comunicarse con el proceso de UWP mediante un servicio de aplicaciones. Tenemos muchos consejos para darte sobre cómo configurarlo. Si quieres agregar una experiencia que requiere un proyecto de UWP, consulta Ampliación con componentes de UWP.

Consulte los contratos de API

Si puedes llamar a la API directamente desde tu aplicación de escritorio, abre un navegador y busca el tema de referencia para esa API. Debajo del resumen de la API, encontrarás una tabla en la que se describe el contrato de API para esa API. Este es un ejemplo de esa tabla:

API contract table

Si tienes una aplicación de escritorio basada en .NET, agrega una referencia a ese contrato de API y luego establece la propiedad Copia local de ese archivo en False. Si tienes un proyecto basado en C++, agrega a Directorios de inclusión adicionales una ruta de acceso a la carpeta que contiene este contrato.

Llame a las API para agregar la experiencia

Este es el código que usarías para mostrar la ventana de notificación que hemos visto anteriormente. Estas API aparecen en esta lista para que puedas agregar este código a la aplicación de escritorio y ejecutarlo en este momento.

using Windows.Foundation;
using Windows.System;
using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;
...

private void ShowToast()
{
    string title = "featured picture of the day";
    string content = "beautiful scenery";
    string image = "https://picsum.photos/360/180?image=104";
    string logo = "https://picsum.photos/64?image=883";

    string xmlString =
    $@"<toast><visual>
       <binding template='ToastGeneric'>
       <text>{title}</text>
       <text>{content}</text>
       <image src='{image}'/>
       <image src='{logo}' placement='appLogoOverride' hint-crop='circle'/>
       </binding>
      </visual></toast>";

    XmlDocument toastXml = new XmlDocument();
    toastXml.LoadXml(xmlString);

    ToastNotification toast = new ToastNotification(toastXml);

    ToastNotificationManager.CreateToastNotifier().Show(toast);
}
#include <sstream>
#include <winrt/Windows.Data.Xml.Dom.h>
#include <winrt/Windows.UI.Notifications.h>

using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Notifications;
using namespace winrt::Windows::Data::Xml::Dom;

void UWP::ShowToast()
{
    std::wstring const title = L"featured picture of the day";
    std::wstring const content = L"beautiful scenery";
    std::wstring const image = L"https://picsum.photos/360/180?image=104";
    std::wstring const logo = L"https://picsum.photos/64?image=883";

    std::wostringstream xmlString;
    xmlString << L"<toast><visual><binding template='ToastGeneric'>" <<
        L"<text>" << title << L"</text>" <<
        L"<text>" << content << L"</text>" <<
        L"<image src='" << image << L"'/>" <<
        L"<image src='" << logo << L"'" <<
        L" placement='appLogoOverride' hint-crop='circle'/>" <<
        L"</binding></visual></toast>";

    XmlDocument toastXml;

    toastXml.LoadXml(xmlString.str().c_str());

    ToastNotificationManager::CreateToastNotifier().Show(ToastNotification(toastXml));
}
using namespace Windows::Foundation;
using namespace Windows::System;
using namespace Windows::UI::Notifications;
using namespace Windows::Data::Xml::Dom;

void UWP::ShowToast()
{
	Platform::String ^title = "featured picture of the day";
	Platform::String ^content = "beautiful scenery";
	Platform::String ^image = "https://picsum.photos/360/180?image=104";
	Platform::String ^logo = "https://picsum.photos/64?image=883";

	Platform::String ^xmlString =
		L"<toast><visual><binding template='ToastGeneric'>" +
		L"<text>" + title + "</text>" +
		L"<text>"+ content + "</text>" +
		L"<image src='" + image + "'/>" +
		L"<image src='" + logo + "'" +
		L" placement='appLogoOverride' hint-crop='circle'/>" +
		L"</binding></visual></toast>";

	XmlDocument ^toastXml = ref new XmlDocument();

	toastXml->LoadXml(xmlString);

	ToastNotificationManager::CreateToastNotifier()->Show(ref new ToastNotification(toastXml));
}

Para obtener más información acerca de notificaciones, consulta Notificaciones del sistema interactivas y adaptables.

Compatibilidad con bases de instalación de Windows XP, Windows Vista y Windows 7/8

Puedes modernizar tu aplicación para Windows 10 sin tener que crear una nueva rama ni mantener bases de código independientes.

Si quieres compilar archivos binarios independientes para los usuarios de Windows 10, usa la compilación condicional. Si prefieres compilar un conjunto de archivos binarios que implementas en todos los usuarios de Windows, usa las comprobaciones en tiempo de ejecución.

Echemos un vistazo rápido a cada opción.

Compilación condicional

Puedes mantener una base de código y compilar un conjunto de archivos binarios solo para los usuarios de Windows 10.

En primer lugar, agrega una nueva configuración de compilación al proyecto.

Build Configuration

Para esa configuración de compilación, cree una constante para identificar el código que llama a las API de WinRT.

Para los proyectos basados en .NET, la constante se llama constante de compilación condicional.

Conditional Compilation constant

Para los proyectos basados en C++, la constante se llama definición del preprocesador.

Preprocessor Definition constant

Agrega esa constante antes de cualquier bloque de código UWP.

[System.Diagnostics.Conditional("_UWP")]
private void ShowToast()
{
 ...
}
#if _UWP
void UWP::ShowToast()
{
 ...
}
#endif

El compilador compila ese código solo si esa constante está definida en la configuración de compilación activa.

Comprobaciones en tiempo de ejecución

Puedes compilar un conjunto de archivos binarios para todos tus usuarios de Windows independientemente de qué versión de Windows ejecuten. La aplicación solo llama a las API de WinRT si el usuario ejecuta la aplicación como una aplicación empaquetada en Windows 10.

La manera más sencilla de agregar comprobaciones en tiempo de ejecución en el código es instalar este paquete de Nuget: Aplicaciones auxiliares de Puente de dispositivo de escritorio y usar luego el método IsRunningAsUWP() para desactivar todo el código que llama a las API de WinRT. Consulte esta entrada de blog para obtener más información: Puente de dispositivo de escritorio: identifica el contexto de la aplicación.

Encontrar respuestas a tus preguntas

¿Tienes alguna pregunta? Pregúntanos en Stack Overflow. Nuestro equipo supervisa estas etiquetas. También puede preguntar en nuestros foros.