Componentes de Windows Runtime con C++/WinRT

En este tema se muestra cómo usar C++/WinRT para crear y consumir un componente de Windows Runtime, un componente al que se puede llamar desde una aplicación universal de Windows compilada con cualquier lenguaje de Windows Runtime.

Hay varias razones para crear un componente de Windows Runtime en C++/WinRT.

  • Para disfrutar de la ventaja de rendimiento de C++ en operaciones complejas o de uso intensivo computacional.
  • Para reutilizar el código estándar de C++ que ya está escrito y probado.
  • Para exponer la funcionalidad win32 a una aplicación de Plataforma universal de Windows (UWP) escrita en, por ejemplo, C#.

En general, al crear el componente de C++/WinRT, puede usar tipos de la biblioteca estándar de C++ y tipos integrados, excepto en el límite de la interfaz binaria de la aplicación (ABI) donde se pasan datos al código de otro .winmd paquete y desde este. En la ABI, use los tipos de Windows Runtime. Además, en el código de C++/WinRT, use tipos como delegado y evento para implementar eventos que se pueden generar desde el componente y controlarse en otro lenguaje. Consulta C++/WinRT para obtener más información sobre C++/WinRT.

El resto de este tema te guía a través de cómo crear un componente de Windows Runtime en C++/WinRT y, a continuación, cómo consumirlo desde una aplicación.

El componente de Windows Runtime que compilarás en este tema contiene una clase en tiempo de ejecución que representa un termómetro. En el tema también se muestra una aplicación principal que consume la clase en tiempo de ejecución de termómetro y llama a una función para ajustar la temperatura.

Nota:

Para más información sobre cómo instalar y usar C++/WinRT Visual Studio Extension (VSIX) y el paquete de NuGet (que juntos proporcionan la plantilla de proyecto y compatibilidad de la compilación), consulta Compatibilidad de Visual Studio para C++/WinRT.

Importante

Para conocer los conceptos y términos esenciales que te ayuden a entender cómo consumir y crear clases en tiempo de ejecución con C++/WinRT, consulta Consumir API con C++/WinRT y Crear API con C++/WinRT .

Procedimiento recomendado de nomenclatura para dll de componentes de Windows Runtime

Importante

En esta sección se describe la convención de nomenclatura que se recomienda usar para el archivo (DLL) en el que se compila el .dll componente de Windows Runtime. Se trata de la secuencia de activación que sigue C++/WinRT cuando se consume una clase en tiempo de ejecución de un componente de Windows Runtime.

Al activar un generador de clases, C++/WinRT intenta primero una llamada a RoGetActivationFactory. Si se produce un error, C++/WinRT intenta buscar un archivo DLL que se cargue directamente. La activación de Windows Runtime siempre se basa en un nombre de clase completo. La lógica consiste en quitar el nombre de clase (de ese nombre de clase completo) y, a continuación, buscar un archivo DLL denominado para el espacio de nombres completo que permanece. Si no se encuentra, quite el nombre del segmento más específico y repita.

Por ejemplo, si la clase que se activa tiene un nombre completo de Contoso.Instruments.ThermoWRC.Thermo, y RoGetActivationFactory produce un error, primero buscaremos un Contoso.Instruments.ThermometerWRC.dll. Si no se encuentra, buscaremos Contoso.Instruments.dlly, a continuación, para Contoso.dll.

Cuando se encuentra un archivo DLL (en esa secuencia), usaremos el punto de entrada DllGetActivationFactory de esa DLL para intentar obtener el generador directamente (en lugar de indirectamente a través de la función RoGetActivationFactory que intentamos por primera vez). Incluso así, el resultado final es indistinguible para el autor de la llamada y para el archivo DLL.

Este proceso es completamente automático: no se necesita ningún registro o herramienta. Si vas a crear un componente de Windows Runtime, solo tienes que usar una convención de nomenclatura para los archivos DLL que funcionen con el proceso que acaba de describir. Y si estás consumiendo un componente de Windows Runtime y no se llama correctamente, tienes la opción de cambiarle el nombre tal como se describe.

Crear un componente de Windows Runtime (ThermoWRC)

Para empezar, crea un proyecto en Microsoft Visual Studio. Cree un proyecto componente de Windows Runtime (C++/WinRT) y asígneles el nombre ThermoWRC (para el componente "termómetro de Windows Runtime"). Asegúrese de que la opción Colocar la solución y el proyecto en el mismo directorio esté desactivada. Elija como destino la versión más reciente disponible de manera general (es decir, no en versión preliminar) de Windows SDK. Asignar un nombre al proyecto ThermoWRC le proporcionará la experiencia más sencilla con el resto de los pasos de este tema.

No compile aún el proyecto.

El proyecto recién creado contiene un archivo llamado Class.idl. En el Explorador de soluciones, cambie el nombre del archivo Thermometer.idl (al cambiar el nombre del archivo .idl, se cambia automáticamente el nombre de los archivos .h y .cpp dependientes). Reemplaza el contenido de Thermometer.idl por la lista siguiente.

// Thermometer.idl
namespace ThermometerWRC
{
    runtimeclass Thermometer
    {
        Thermometer();
        void AdjustTemperature(Single deltaFahrenheit);
    };
}

Guarde el archivo. El proyecto no se compilará hasta su finalización en este momento, pero compilar ahora es algo útil, ya que genera los archivos de código fuente en los que implementará la clase en tiempo de ejecución De termómetro . Así que puedes compilar ahora. En esta fase, verás errores de compilación porque no se han encontrado Class.h y Class.g.h.

Durante el proceso de compilación, la herramienta midl.exe se ejecuta para crear el archivo de metadatos de Windows Runtime de tu componente, que es \ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd. Después se ejecutará la herramienta cppwinrt.exe (con la opción -component) para generar archivos de código fuente y ayudarte a crear tu componente. Estos archivos incluyen códigos auxiliares para empezar a implementar la clase en tiempo de ejecución De termómetro que declaró en su IDL. Estos archivos de código auxiliar son \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.h y Thermometer.cpp

Haz clic con el botón derecho en el nodo del proyecto y haz clic en Abrir carpeta en el Explorador de archivos. Se abre la carpeta del proyecto en el Explorador de archivos. Allí, copia los archivos de código auxiliar Thermometer.h y Thermometer.cpp desde la carpeta \ThermometerWRC\ThermometerWRC\Generated Files\sources\ en la carpeta que contiene los archivos de proyecto, que es \ThermometerWRC\ThermometerWRC\, y reemplaza los archivos que hay en el destino. Ahora, vamos a abrir Thermometer.h y Thermometer.cpp e implementar nuestra clase en tiempo de ejecución. En Thermometer.h, agregue un nuevo miembro privado a la implementación (no a la implementación de fábrica) del termómetro.

// Thermometer.h
...
namespace winrt::ThermometerWRC::implementation
{
    struct Thermometer : ThermometerT<Thermometer>
    {
        ...

    private:
        float m_temperatureFahrenheit { 0.f };
    };
}
...

En Thermometer.cpp, implemente el método AdjustTemperature como se muestra en la lista siguiente.

// Thermometer.cpp
...
namespace winrt::ThermometerWRC::implementation
{
    void Thermometer::AdjustTemperature(float deltaFahrenheit)
    {
        m_temperatureFahrenheit += deltaFahrenheit;
    }
}

Verá static_assert en la parte superior de Thermometer.h y Thermometer.cpp, que deberás quitar. Ahora se compilará el proyecto.

Si las advertencias te impiden compilar, resuélvelas o establece la propiedad de proyecto C/C++>General>Tratar advertencias como errores en No (/WX-) y vuelve a compilar el proyecto.

Crear una aplicación principal (ThermoCoreApp) para probar el componente de Windows Runtime

Ahora cree un nuevo proyecto (ya sea en la solución ThermoWRC o en uno nuevo). Cree un proyecto core App (C++/WinRT) y asígnelo el nombre ThermoCoreApp. Establezca ThermoCoreApp como proyecto de inicio si los dos proyectos están en la misma solución.

Nota:

Como se mencionó anteriormente, el archivo de metadatos de Windows Runtime para el componente de Windows Runtime (cuyo proyecto denominado ThermoWRC) se crea en la carpeta \ThermometerWRC\Debug\ThermometerWRC\. El primer segmento de esa ruta de acceso es el nombre de la carpeta que contiene el archivo de solución; el siguiente segmento es el subdirectorio de la que se denomina Debug; y el último segmento es el subdirectorio de la asignada para el componente de Windows Runtime. Si no ha nombre al proyecto ThermoWRC, el archivo de metadatos estará en la carpeta \<YourProjectName>\Debug\<YourProjectName>\.

Ahora, en el proyecto Core App (ThermoCoreApp), agregue una referencia y vaya a \ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd (o agregue una referencia de proyecto a proyecto, si los dos proyectos están en la misma solución). Haz clic en Agregar y después en Aceptar. Ahora compile ThermoCoreApp. En el improbable caso de que vea un error que indica que el archivo readme.txt de carga no existe, excluya ese archivo del proyecto de componentes de Windows Runtime, recompile y vuelva a generar ThermoCoreApp.

Durante el proceso de compilación, la herramienta cppwinrt.exe se ejecutará para procesar el archivo .winmd al que se hace referencia en los archivos de código fuente que contienen tipos proyectados para ayudarte a consumir tu componente. El encabezado de los tipos proyectados para las clases en tiempo de ejecución del componente, denominadas ThermometerWRC.h, se genera en la carpeta \ThermometerCoreApp\ThermometerCoreApp\Generated Files\winrt\.

Incluye dicho encabezado en App.cpp.

// App.cpp
...
#include <winrt/ThermometerWRC.h>
...

Además, en App.cpp, agregue el código siguiente para crear una instancia de un objeto Termómetro (mediante el constructor predeterminado del tipo proyectado) y llame a un método en el objeto termómetro.

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer m_thermometer;
    ...
    
    void OnPointerPressed(IInspectable const &, PointerEventArgs const & args)
    {
        m_thermometer.AdjustTemperature(1.f);
        ...
    }
    ...
};

Cada vez que haga clic en la ventana, incrementará la temperatura del objeto de termómetro. Puedes establecer puntos de interrupción si quieres recorrer el código para confirmar que la aplicación realmente llama al componente de Windows Runtime.

Pasos siguientes

Para agregar aún más funcionalidad, o nuevos tipos de Windows Runtime, al componente de Windows Runtime de C++/WinRT, puedes seguir los mismos patrones que se muestran anteriormente. En primer lugar, use IDL para definir la funcionalidad que desea exponer. A continuación, compile el proyecto en Visual Studio para generar una implementación de código auxiliar. Después, complete la implementación según corresponda. Todos los métodos, propiedades y eventos que defina en IDL son visibles para la aplicación que consume el componente de Windows Runtime. Para obtener más información sobre IDL, consulta Introducción al lenguaje de definición de interfaz de Microsoft 3.0.

Para obtener un ejemplo de cómo agregar un evento al componente de Windows Runtime, consulta Crear eventos en C++/WinRT.

Solución de problemas

Síntoma Solución
En una aplicación de C++ o WinRT, al consumir un componente de Windows Runtime para C# que usa XAML, el compilador genera un error con el formato "'MyNamespace_XamlTypeInfo': no es miembro de 'winrt::MyNamespace'" donde MyNamespace es el nombre del espacio de nombres del componente de Windows Runtime. En el archivo pch.h de la aplicación que consume C++ o WinRT, agregue #include <winrt/MyNamespace.MyNamespace_XamlTypeInfo.h> y reemplace MyNamespace según corresponda.