Introducción a C++/WinRT

Important

Para obtener información sobre cómo configurar Visual Studio para el desarrollo de C++/WinRT, incluida la instalación y el uso de la extensión de Visual Studio de C++/WinRT (VSIX) y el paquete NuGet (que juntos proporcionan compatibilidad con la plantilla de proyecto y la compilación), consulte Visual Studio compatibilidad con C++/WinRT.

Para ponerse al día con el uso de C++/WinRT, este tema le guía a través de un ejemplo de código sencillo basado en un nuevo proyecto de aplicación de consola de Windows (C++/WinRT). En este tema también se muestra cómo agregar compatibilidad con C++/WinRT a un proyecto de aplicación de Windows Escritorio.

Note

Aunque se recomienda desarrollar con las versiones más recientes de Visual Studio y el SDK de Windows, si usa Visual Studio 2017 (versión 15.8.0 o posterior) y tiene como destino la versión 10.0.17134.0 del SDK de Windows (Windows 10, versión 1803), después un proyecto de C++/WinRT recién creado puede no compilarse con el error "error C3861: 'from_abi': identificador no encontrado" y con otros errores que se originan en base.h. La solución es usar como destino una versión posterior (más conforme) del SDK de Windows o establecer la propiedad del proyecto C/C++>Idioma>Modo de conformidad: No (además, si /permissive- aparece en la propiedad del proyecto C/C++>Idioma>Línea de comandos, en Opciones adicionales, elimínelo).

Inicio rápido de C++/WinRT

Cree un nuevo proyecto de aplicación de consola de Windows (C++/WinRT).

Edite pch.h y main.cpp para que tengan este aspecto.

// pch.h
#pragma once
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>
#include <iostream>
// main.cpp
#include "pch.h"

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;

int main()
{
    winrt::init_apartment();

    Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    SyndicationClient syndicationClient;
    syndicationClient.SetRequestHeader(L"User-Agent", L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
    SyndicationFeed syndicationFeed = syndicationClient.RetrieveFeedAsync(rssFeedUri).get();
    for (const SyndicationItem syndicationItem : syndicationFeed.Items())
    {
        winrt::hstring titleAsHstring = syndicationItem.Title().Text();
        
        // A workaround to remove the trademark symbol from the title string, because it causes issues in this case.
        std::wstring titleAsStdWstring{ titleAsHstring.c_str() };
        titleAsStdWstring.erase(remove(titleAsStdWstring.begin(), titleAsStdWstring.end(), L'™'), titleAsStdWstring.end());
        titleAsHstring = titleAsStdWstring;

        std::wcout << titleAsHstring.c_str() << std::endl;
    }
}

Vamos a tomar el breve ejemplo de código anterior por fragmento y explicar lo que sucede en cada parte.

#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>

Con la configuración predeterminada del proyecto, los encabezados incluidos proceden del SDK de Windows, dentro de la carpeta %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt. Visual Studio incluye esa ruta de acceso en su macro IncludePath. Pero no hay ninguna dependencia estricta en el SDK de Windows, ya que el proyecto (a través de la herramienta) genera esos mismos encabezados en la cppwinrt.exe carpeta $(GeneratedFilesDir) del proyecto. Se cargarán desde esa carpeta si no se encuentran en otro lugar o si cambia la configuración del proyecto.

Los encabezados contienen las API de Windows proyectadas a C++/WinRT. En otras palabras, para cada tipo de Windows, C++/WinRT define un equivalente descriptivo de C++(denominado tipo proyectado). Un tipo proyectado tiene el mismo nombre completo que el tipo Windows, pero se coloca en el espacio de nombres winrt de C++. Al colocar estos elementos en el encabezado precompilado, se reducen los tiempos de compilación incrementales.

Important

Siempre que desees usar un tipo de un espacio de nombres de Windows, debes #include incluir el archivo de encabezado correspondiente del espacio de nombres de Windows para C++/WinRT, como se muestra arriba. El encabezado correspondiente es el que tiene el mismo nombre que el espacio de nombres del tipo. Por ejemplo, para usar la proyección de C++/WinRT para la clase en tiempo de ejecución Windows::Foundation::Collections::PropertySet, incluya el archivo de encabezado winrt/Windows.Foundation.Collections.h.

Es habitual que un encabezado de proyección de C++/WinRT incluya automáticamente archivos de encabezado de espacio de nombres relacionados. Por ejemplo, winrt/Windows.Foundation.Collections.h incluye winrt/Windows.Foundation.h. Pero no debe basarse en este comportamiento, ya que es un detalle de implementación que cambia con el tiempo. Debe incluir explícitamente cualquier encabezado que necesite.

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;

Las using namespace directivas son opcionales, pero convenientes. El patrón mostrado anteriormente en el caso de estas directivas (permiten la búsqueda no calificada de nombres de cualquier elemento del espacio de nombres winrt) es adecuado cuando se inicia un proyecto nuevo y C++/WinRT es la única proyección de lenguaje que se utiliza en ese proyecto. Por otro lado, estás mezclando código de C++/WinRT con código de C++/CX y/o código de la interfaz binaria de la aplicación del SDK (ABI) (ya sea portar o interoperar con uno o ambos modelos), consulta los temas Interoperabilidad entre C++/WinRT y C++/CX, Mover a C++/WinRT desde C++/CX e Interoperabilidad entre C++/WinRT y la ABI.

winrt::init_apartment();

La llamada a winrt::init_apartment inicializa el subproceso en el Windows Runtime; de forma predeterminada, en un apartamento multiproceso. La llamada también inicializa COM.

Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
SyndicationClient syndicationClient;

Asigna en la pila dos objetos: representan el URI del blog de Windows y un cliente de sindicación. Creamos el URI con un literal de cadena ancha simple (consulta Control de cadenas en C++/WinRT para obtener más formas de trabajar con cadenas).

SyndicationFeed syndicationFeed = syndicationClient.RetrieveFeedAsync(rssFeedUri).get();

SyndicationClient::RetrieveFeedAsync es un ejemplo de una función de Windows Runtime asincrónica. El ejemplo de código recibe un objeto de operación asíncrona de RetrieveFeedAsync e invoca get en ese objeto para bloquear el hilo que realiza la llamada y esperar el resultado (que, en este caso, es una fuente de sindicación). Para obtener más información sobre la simultaneidad y para las técnicas de no bloqueo, consulte Operaciones asincrónicas y de simultaneidad con C++/WinRT.

for (const SyndicationItem syndicationItem : syndicationFeed.Items()) { ... }

SyndicationFeed.Items es un intervalo definido por los iteradores devueltos desde las funciones begin y end (o sus variantes constantes, inversas y inversas constantes). Debido a esto, puede enumerar Items con una instrucción for basada en intervalos o con la función de plantilla std::for_each. Siempre que recorra una colección de Windows Runtime de este modo, tendrá que #include <winrt/Windows.Foundation.Collections.h>.

winrt::hstring titleAsHstring = syndicationItem.Title().Text();

// Omitted: there's a little bit of extra work here to remove the trademark symbol from the title text.

std::wcout << titleAsHstring.c_str() << std::endl;

Obtiene el texto del título del canal, como un objeto winrt::hstring (más detalles en Administración de cadenas en C++/WinRT). A continuación, el hstring se genera a través de la función c_str , que refleja el patrón usado con cadenas de la biblioteca estándar de C++.

Como puede ver, C++/WinRT fomenta expresiones modernas y similares a clases, como syndicationItem.Title().Text(). Este es un estilo de programación diferente y más limpio de la programación COM tradicional. No es necesario inicializar com directamente ni trabajar con punteros COM.

Tampoco necesita manejar los códigos de retorno HRESULT. C++/WinRT convierte los HRESULT de error en excepciones como winrt::hresult-error para un estilo de programación natural y moderno. Para obtener más información sobre el control de errores y ejemplos de código, consulta Control de errores con C++/WinRT.

Modificación de un proyecto de aplicación de escritorio de Windows para agregar compatibilidad con C++/WinRT

Algunos proyectos de escritorio (por ejemplo, las plantillas de WinUI 3 de Visual Studio) tienen compatibilidad integrada con C++/WinRT.

Pero en esta sección se muestra cómo agregar compatibilidad con C++/WinRT a cualquier proyecto de aplicación de escritorio de Windows que pueda tener. Si no tiene un proyecto de aplicación de escritorio de Windows existente, puede seguir estos pasos creando uno primero. Por ejemplo, abra Visual Studio y cree un proyecto de Visual C++>Windows Desktop>Aplicación de escritorio de Windows.

Opcionalmente, puede instalar la extensión de Visual Studio (VSIX) de C++/WinRT y el paquete NuGet. Para obtener más información, consulte Visual Studio compatibilidad con C++/WinRT.

Configuración de propiedades del proyecto

Vaya a la propiedad del proyecto General>Windows versión del SDK y seleccione Todas las configuraciones y todas las plataformas. Asegúrese de que Windows versión del SDK esté establecida en 10.0.17134.0 (Windows 10, versión 1803) o posterior.

Confirme que no se ve afectado por ¿Por qué no se compilará mi nuevo proyecto?

Dado que C++/WinRT utiliza características del estándar C++17, establezca la propiedad del proyecto C/C++>Language>Estándar del lenguaje C++ en Estándar ISO C++17 (/std:c++17).

Encabezado precompilado

La plantilla de proyecto predeterminada crea un encabezado precompilado para usted, denominado framework.h, o stdafx.h. Cambie el nombre a pch.h. Si tiene un stdafx.cpp archivo, cambie el nombre a pch.cpp. Establezca la propiedad del proyecto C/C++>Encabezados precompilados>Encabezado precompilado en Crear (/Yc), y Archivo de encabezados precompilados en pch.h.

Busque y reemplace todos #include "framework.h" (o #include "stdafx.h") por #include "pch.h".

En pch.h, incluya winrt/base.h.

// pch.h
...
#include <winrt/base.h>

Linking

La proyección de lenguaje de C++/WinRT depende de determinadas funciones libres de Windows Runtime (que no son miembros) y de puntos de entrada que requieren enlazar con la biblioteca contenedora WindowsApp.lib. En esta sección se describen tres maneras de satisfacer el enlazador.

La primera opción es agregar al proyecto de Visual Studio todas las propiedades y destinos de MSBuild de C++/WinRT. Para ello, instale el paquete NuGet Microsoft.Windows.CppWinRT en su proyecto. Abra el project en Visual Studio, haga clic en Project>Administrar paquetes NuGet...>Examine, escriba o pegue Microsoft.Windows. CppWinRT en el cuadro de búsqueda, seleccione el elemento en los resultados de búsqueda y, a continuación, haga clic en Instalar para instalar el paquete para ese project.

También puede usar la configuración de vínculos del proyecto para vincular explícitamente WindowsApp.lib. O bien, puede hacerlo en el código fuente (en pch.h, por ejemplo) como este.

#pragma comment(lib, "windowsapp")

Ahora puede compilar y vincular y agregar código de C++/WinRT al proyecto (por ejemplo, código similar al que se muestra en la sección Inicio rápido de A C++/WinRT , arriba).

Los tres escenarios principales para C++/WinRT

A medida que use y se familiarice con C++/WinRT, y trabaje con el resto de la documentación aquí, es probable que observe que hay tres escenarios principales, como se describe en las secciones siguientes.

Consumo de API y tipos de Windows

En otras palabras, usar, o llamar a las API. Por ejemplo, realizar llamadas API para comunicarse mediante Bluetooth; para transmitir y presentar vídeo; para integrarse con el shell de Windows, etc. C++/WinRT es totalmente compatible con esta categoría de escenario. Para obtener más información, consulta Consumir API con C++/WinRT.

Creación de API y tipos de Windows

En otras palabras, generar API y tipos. Por ejemplo, generar los tipos de API que se describen en la sección anterior; o las API de gráficos; las API de almacenamiento y sistema de archivos; las API de red, etc. Para obtener más información, consulta Creación de API con C++/WinRT.

Crear API con C++/WinRT es un poco más complejo que consumirlas, porque primero hay que usar IDL para definir la estructura de la API antes de poder implementarla. Hay un tutorial sobre cómo hacerlo en controles XAML; enlazar a una propiedad de C++/WinRT.

Aplicaciones XAML

Este escenario consiste en crear aplicaciones y controles en el marco de interfaz de usuario XAML. Trabajar en una aplicación XAML equivale a una combinación de consumo y creación. Pero dado que XAML es el marco de interfaz de usuario dominante en Windows hoy, y su influencia sobre la Windows Runtime es proporcional a eso, merece su propia categoría de escenario.

Ten en cuenta que XAML funciona mejor con lenguajes de programación que ofrecen reflexión. En C++/WinRT, a veces tienes que hacer un poco de trabajo adicional para interoperar con el marco XAML. Todos esos casos se tratan en la documentación. Los buenos lugares para empezar son controles XAML; enlazar a una propiedad de C++/WinRT y controles personalizados XAML (con plantilla) con C++/WinRT.

Aplicaciones de ejemplo escritas en C++/WinRT

Consulte ¿Dónde puedo encontrar aplicaciones de ejemplo de C++/WinRT?

API importantes