Preguntas más frecuentes sobre C++/WinRT

Respuestas a preguntas que probablemente tenga sobre cómo desarrollar y consumir las API de Windows Runtime con C++/WinRT.

Important

Para consultar las notas de la versión de C++/WinRT, consulta Noticias y cambios en C++/WinRT 2.0.

Note

Si su pregunta trata sobre un mensaje de error que ha visto, consulte también el tema Solución de problemas de C++/WinRT .

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

¿Cómo reegeto mi proyecto de C++/WinRT a una versión posterior del SDK de Windows?

¿Por qué no se compilará mi nuevo proyecto, ahora que he pasado a C++/WinRT 2.0?

Para obtener el conjunto completo de cambios (incluidos los cambios importantes), consulte Noticias y cambios en C++/WinRT 2.0. Por ejemplo, si usa un for basado en intervalos en una colección de Windows Runtime, ahora tendrá que #include <winrt/Windows.Foundation.Collections.h>.

¿Por qué no se compilará mi nuevo proyecto? Uso Visual Studio 2017 (versión 15.8.0 o posterior) y sdk versión 17134

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 consiste en tener como destino una versión posterior (más conforme) del SDK de Windows o establecer la propiedad del proyecto C/C++>Language>Conformance mode: No (también, si /permissive- aparece en la propiedad del proyecto C/C++>Línea de comandos en Opciones adicionales y, a continuación, elimínela).

Cómo se resuelve el error de compilación "El VSIX de C++/WinRT ya no proporciona compatibilidad con la compilación del proyecto. Agregue una referencia de proyecto al paquete NuGet Microsoft.Windows.CppWinRT.

Instale el paquete NuGet Microsoft.Windows.CppWinRT en su proyecto. Para obtener más información, consulte Versiones anteriores de la extensión VSIX.

¿Cómo se personaliza la compatibilidad de compilación en el paquete NuGet?

La compatibilidad de compilación de C++/WinRT (props/targets) está documentada en el archivo Léame del paquete NuGet Microsoft.Windows.CppWinRT.

¿Cuáles son los requisitos de la extensión de Visual Studio de C++/WinRT (VSIX)?

Para la versión 1.0.190128.4 de la extensión VSIX y versiones posteriores, consulte Visual Studio compatibilidad con C++/WinRT. Para otras versiones, consulte Versiones anteriores de la extensión VSIX.

¿Qué es una clase de tiempo de ejecución?

Una clase en tiempo de ejecución es un tipo que se puede activar y consumir a través de interfaces COM modernas, normalmente a través de límites ejecutables. Sin embargo, una clase en tiempo de ejecución también se puede usar dentro de la unidad de compilación que la implementa. Declara una clase en tiempo de ejecución en el lenguaje de definición de interfaz (IDL) y puede implementarla en C++ estándar mediante C++/WinRT.

¿Qué significa el tipo proyectado y el tipo de implementación?

Si solo usa una clase de Windows Runtime (clase en tiempo de ejecución), trabajará exclusivamente con tipos proyectados. C++/WinRT es una proyección de lenguaje, por lo que los tipos proyectados forman parte de la superficie del Windows Runtime que se proyecta en C++ con C++/WinRT. Para obtener más información, consulte Consumir API con C++/WinRT.

El tipo de implementación contiene la implementación de una clase en tiempo de ejecución, por lo que solo está disponible en el proyecto que implementa la clase en tiempo de ejecución. Cuando trabajas en un proyecto que implementa clases en tiempo de ejecución (un proyecto de componente de Windows Runtime o un proyecto que usa la interfaz de usuario XAML), es importante estar cómodo con la distinción entre el tipo de implementación de una clase en tiempo de ejecución y el tipo proyectado que representa la clase en tiempo de ejecución proyectada en C++/WinRT. Para obtener más información, consulte Creación de API con C++/WinRT.

¿Es necesario declarar un constructor en el IDL de mi clase de tiempo de ejecución?

Solo si la clase de tiempo de ejecución está diseñada para ser utilizada desde fuera de la unidad de compilación que la implementa (es un componente de Windows Runtime destinado al uso general por parte de las aplicaciones cliente de Windows Runtime). Para obtener información completa sobre el propósito y las consecuencias de declarar constructores en IDL, consulte Constructores de clase en tiempo de ejecución.

¿Por qué el compilador muestra el error "C3779: consume_Something: la función que devuelve 'auto' no se puede usar antes de que se defina"?

Está usando un objeto Windows Runtime sin haber incluido primero el archivo de encabezado del espacio de nombres correspondiente. Incluya el archivo de encabezado correspondiente al espacio de nombres de la API y vuelva a compilar. Para obtener más información, consulta Encabezados de proyección de C++/WinRT.

¿Por qué el enlazador me da un error de "LNK2019: símbolo externo sin resolver"

Si el símbolo sin resolver es una función gratuita de Windows Runtime, como RoInitialize, deberá vincular explícitamente la biblioteca paraguas WindowsApp.lib en el proyecto. La proyección de C++/WinRT depende de algunas de estas funciones libres (no miembro) y puntos de entrada. Si usa una de las plantillas de proyecto de la extensión de Visual Studio (VSIX) para C++/WinRT para su aplicación, WindowsApp.lib se vinculará automáticamente. De lo contrario, puede usar la configuración del vínculo del proyecto para incluirla o hacerlo en el código fuente.

#pragma comment(lib, "windowsapp")

Es importante que resuelva cualesquiera errores del vinculador que pueda vinculando WindowsApp.lib en lugar de una biblioteca alternativa de enlace estático; de lo contrario, su aplicación no superará las pruebas del Aplicación de Windows Certification Kit que usan Visual Studio y Microsoft Store para validar los envíos, lo que significa que, en consecuencia, no será posible incorporar correctamente su aplicación a Microsoft Store.

Si el símbolo sin resolver es un constructor, es posible que haya olvidado incluir el archivo de encabezado del espacio de nombres para la clase que se está construyendo. Incluya el encabezado denominado para el espacio de nombres de la clase y recompile. Para obtener más información, consulta Encabezados de proyección de C++/WinRT.

¿Por qué obtengo una excepción de "clase no registrada"?

En este caso, el síntoma es que, al crear una clase en tiempo de ejecución o al acceder a un miembro estático, se produce una excepción en tiempo de ejecución con el valor HRESULT REGDB_E_CLASSNOTREGISTERED.

Una causa puede ser que el componente de Windows Runtime no se pueda cargar. Asegúrese de que el archivo de metadatos Windows Runtime del componente (.winmd) tenga el mismo nombre que el binario del componente (el .dll), que también es el nombre del proyecto y el nombre del espacio de nombres raíz. Asegúrese también de que los metadatos de Windows Runtime y el binario se hayan copiado correctamente durante el proceso de compilación en la carpeta Appx de la aplicación que los consume. Y confirme que la aplicación cliente AppxManifest.xml (también en la carpeta Appx) contiene un elemento <InProcessServer> que declara correctamente la clase activable y el nombre del binario.

Construcción uniforme Este error también puede producirse si intenta crear una instancia de una clase en tiempo de ejecución implementada localmente a través de cualquiera de los constructores del tipo proyectado (excepto su constructor std::nullptr_t ). Para ello, necesitarás la característica C++/WinRT 2.0 que a menudo se denomina construcción uniforme. Si desea participar en esa característica, para obtener más información y ejemplos de código, consulte Participación en la construcción uniforme y acceso directo a la implementación.

Para obtener una forma de crear instancias de las clases en tiempo de ejecución implementadas localmente que no requieren construcción uniforme, consulta Controles XAML; enlazar a una propiedad de C++/WinRT.

¿Debo implementar Windows::Foundation::IClosable y, si es así, cómo?

Si tiene una clase en tiempo de ejecución que libera recursos en su destructor y esa clase en tiempo de ejecución está diseñada para consumirse desde fuera de su unidad de compilación de implementación (es un componente de Windows Runtime destinado al consumo general por Windows Runtime aplicaciones cliente), se recomienda implementar también IClosable. para admitir el consumo de la clase en tiempo de ejecución por lenguajes que carecen de finalización determinista. Asegúrese de que sus recursos se liberen, ya sea que se llame al destructor, a IClosable::Close o a ambos. IClosable::Close puede llamarse un número arbitrario de veces.

¿Necesito llamar a IClosable::Close en las clases del entorno de ejecución que uso?

IClosable existe para admitir idiomas que carecen de finalización determinista. Por lo tanto, en general, no es necesario llamar a IClosable::Close desde C++/WinRT. Pero tenga en cuenta estas excepciones a esa regla general.

  • Hay casos muy raros que implican carreras de apagado o adopción semi mortal, donde se necesita llamar a IClosable::Close. Si usa tipos de Windows.UI.Composition, por ejemplo, puede encontrarse con casos en los que quiera liberar objetos en un orden determinado, en lugar de dejar que la destrucción del contenedor de C++/WinRT lo haga por usted.
  • Si no puede garantizar que tenga la última referencia restante a un objeto (ya que la ha pasado a otras API, que podría mantener una referencia), llamar a IClosable::Close es una buena idea.
  • En caso de duda, se puede llamar a IClosable::Close manualmente con seguridad, en lugar de esperar a que el envoltorio lo llame al destruirse.

Así que, si sabe que tiene la última referencia, puede dejar que el destructor del envoltorio se encargue del trabajo. Si necesita cerrar antes de que la última referencia desaparezca, debe llamar a Close. Para que sea seguro frente a excepciones, debería encapsular Close en un tipo de adquisición de recursos es inicialización (RAII) (para que el cierre se produzca durante el desenrollado de la pila). C++/WinRT no tiene un envoltorio unique_close, pero puede crear el suyo propio.

¿Puedo usar LLVM/Clang para compilar con C++/WinRT?

No se admite la cadena de herramientas LLVM y Clang para C++/WinRT, pero hacemos uso de ella internamente para validar la conformidad de los estándares de C++/WinRT. Por ejemplo, si desea emular lo que hacemos internamente, podría probar un experimento como el descrito a continuación.

Vaya a la página de descarga de LLVM, busque Descargar LLVM 6.0.0>binarios precompilados y descargue Clang para Windows (de 64 bits). Durante la instalación, opte por añadir LLVM a la variable de entorno PATH del sistema para poder ejecutar LLVM desde el símbolo del sistema. Para los fines de este experimento, puede omitir los errores "No se pudo encontrar el directorio de conjuntos de herramientas de MSBuild" o "Error de instalación de integración de MSVC", si los ve. Hay una variedad de maneras de invocar LLVM/Clang; En el ejemplo siguiente se muestra solo una manera.

C:\ExperimentWithLLVMClang>type main.cpp
// main.cpp
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "ole32")

#include <winrt/Windows.Foundation.h>
#include <stdio.h>
#include <iostream>

using namespace winrt;

int main()
{
    winrt::init_apartment();
    Windows::Foundation::Uri rssFeedUri{ L"https://blogs.windows.com/feed" };
    std::wcout << rssFeedUri.Domain().c_str() << std::endl;
}

C:\ExperimentWithLLVMClang>clang-cl main.cpp /EHsc /I ..\.. -Xclang -std=c++17 -Xclang -Wno-delete-non-virtual-dtor -o app.exe

C:\ExperimentWithLLVMClang>app
windows.com

Dado que C++/WinRT usa características del estándar de C++17, deberá usar las marcas del compilador necesarias para obtener esa compatibilidad; estas marcas difieren de un compilador a otro.

Visual Studio es la herramienta de desarrollo que se admite y se recomienda para C++/WinRT. Consulte Visual Studio compatibilidad con C++/WinRT.

¿Por qué no tiene el calificador const la función de implementación generada para una propiedad de solo lectura?

Cuando declara una propiedad de solo lectura en MIDL 3.0, podría esperar que la herramienta cppwinrt.exe genere una función de implementación para usted que esté marcada con const (una función const trata el puntero this como const).

Sin duda, se recomienda usar const siempre que sea posible, pero la cppwinrt.exe propia herramienta no intenta razonar sobre qué funciones de implementación podrían ser concebiblemente const y que podrían no ser. Puede elegir establecer cualquiera de las funciones de implementación const, como en este ejemplo.

struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
    winrt::hstring ToString() const
    {
        return L"MyStringable";
    }
};

Puede quitar ese const calificador en ToString si decide que necesita modificar algún estado de objeto en su implementación. Pero haga que cada una de las funciones miembro sea const o no const, no ambas. En otras palabras, no sobrecargue una función de implementación en const.

Además de las funciones de implementación, otro lugar donde const entra en juego es en las proyecciones de funciones de Windows Runtime. Tenga en cuenta este código.

int main()
{
    winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
    auto result{ s.ToString() };
}

Para la llamada anterior a ToString, el comando Ir a declaración de Visual Studio muestra que la proyección de IStringable::ToString de Windows Runtime en C++/WinRT tiene el siguiente aspecto.

winrt::hstring ToString() const;

Las funciones de la proyección son const independientemente de cómo decida calificar su implementación. Entre bastidores, la proyección realiza una llamada a la interfaz binaria de aplicaciones (ABI), lo que equivale a llamar a través de un puntero a interfaz COM. El único estado con el que interactúa el ToString proyectado es ese puntero a la interfaz COM; y, sin duda, no necesita modificar ese puntero, por lo que la función es const. Esto le garantiza que no modificará en modo alguno la referencia a IStringable a través de la que realiza la llamada, y garantiza que puede llamar a ToString incluso con una referencia const a IStringable.

Tenga en cuenta que estos ejemplos de const son detalles de implementación de las proyecciones e implementaciones de C++/WinRT; son prácticas de higiene del código en su beneficio. No existe algo como const en la ABI de COM ni en la de Windows Runtime (en las funciones miembro).

¿Tiene alguna recomendación para reducir el tamaño de código para los archivos binarios de C++/WinRT?

Al trabajar con objetos Windows Runtime, debe evitar el patrón de codificación que se muestra a continuación, ya que puede tener un impacto negativo en la aplicación provocando que se genere más código binario del necesario.

anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();

En el entorno de Windows Runtime, el compilador no puede almacenar en caché ni el valor de c() ni las interfaces de cada método al que se llama mediante una indirección ('.'). A menos que intervengas, eso se traduce en más llamadas virtuales y en la sobrecarga del recuento de referencias. El patrón anterior podría generar fácilmente el doble de código que sea estrictamente necesario. En su lugar, prefiera el patrón que se muestra a continuación siempre que pueda. Genera mucho menos código y también puede mejorar drásticamente el rendimiento del tiempo de ejecución.

auto a{ anobject.b().c() };
a.d();
a.e();
a.f();

El patrón recomendado mostrado anteriormente no solo se aplica a C++/WinRT, sino a todas las proyecciones del lenguaje Windows Runtime.

¿Cómo puedo convertir una cadena en un tipo (por ejemplo, para la navegación)?

Al final del ejemplo de código de vista de navegación (que se encuentra principalmente en C#), hay un fragmento de código de C++/WinRT que muestra cómo hacerlo.

¿Cómo se resuelven las ambigüedades con GetCurrentTime o TRY?

El archivo winrt/Windows.UI.Xaml.Media.Animation.h de encabezado declara un método denominado GetCurrentTime, mientras que windows.h (a través winbase.hde ) define una macro denominada GetCurrentTime. Cuando los dos colisionan, el compilador de C++ genera el "error C4002: Demasiados argumentos para la invocación de macro similar a la función GetCurrentTime".

Del mismo modo, winrt/Windows.Globalization.h declara un método denominado TRY, mientras afx.h define una macro denominada TRY. Cuando estos entran en conflicto, el compilador de C++ genera "error C2334: tokens inesperados antes de '{'; se omite el aparente cuerpo de la función".

Para solucionar uno o ambos problemas, puede hacerlo.

#pragma push_macro("GetCurrentTime")
#pragma push_macro("TRY")
#undef GetCurrentTime
#undef TRY
#include <winrt/include_your_cppwinrt_headers_here.h>
#include <winrt/include_your_cppwinrt_headers_here.h>
#pragma pop_macro("TRY")
#pragma pop_macro("GetCurrentTime")

¿Cómo puedo acelerar la carga de símbolos?

En Visual Studio, en Herramientas>Opciones>Depuración>Símbolos>, active Cargar solo los módulos especificados. A continuación, puede hacer clic con el botón derecho en las DLL de la lista de la pila y cargar módulos individuales.

Note

Si este tema no respondió a su pregunta, puede encontrar ayuda visitando la comunidad de desarrolladores de C++ de Visual Studio o mediante la c++-winrt etiqueta en Stack Overflow.