Responde a perguntas que você provavelmente terá sobre a criação e o consumo de APIs Windows Runtime com C++/WinRT.
Important
Para obter notas sobre o C++/WinRT, consulte Notícias e alterações no C++/WinRT 2.0.
Note
Se a sua pergunta for sobre uma mensagem de erro que você viu, veja também o tópico Solução de problemas do C++/WinRT .
Onde posso encontrar aplicativos de exemplo do C++/WinRT?
Consulte aplicativos de exemplo do C++/WinRT.
Como fazer para redirecionar meu projeto C++/WinRT para uma versão posterior do SDK do Windows?
Por que meu novo projeto não será compilado agora que mudei para o C++/WinRT 2.0?
Para obter o conjunto completo de alterações (incluindo alterações significativas), consulte Notícias e alterações, no C++/WinRT 2.0. Por exemplo, se estiver usando for baseado em intervalo em uma coleção do Windows Runtime, você precisará #include <winrt/Windows.Foundation.Collections.h>.
Por que meu novo projeto não compila? Estou usando Visual Studio 2017 (versão 15.8.0 ou superior) e o SDK versão 17134
Se você estiver usando Visual Studio 2017 (versão 15.8.0 ou superior) e direcionando o SDK Windows versão 10.0.17134.0 (Windows 10, versão 1803), um projeto C++/WinRT recém-criado pode falhar ao compilar com o erro "erro C3861: 'from_abi': identificador não encontrado" e com outros erros originados em base.h. A solução é direcionar para uma versão mais recente (mais compatível com o padrão) do SDK do Windows ou definir a propriedade do projeto C/C++>Linguagem>Modo de conformidade: Não (além disso, se /permissive- aparecer na propriedade do projeto C/C++>Linha de Comando, em Opções Adicionais, exclua-o).
Como corrigir o erro de compilação "O VSIX do C++/WinRT não fornece mais suporte à compilação do projeto." Adicione uma referência de projeto ao pacote Nuget Microsoft.Windows.CppWinRT"?
Instale o pacote NuGet Microsoft.Windows.CppWinRT no seu projeto. Para obter detalhes, consulte versões anteriores da extensão VSIX.
Como personalizar o suporte de build no pacote NuGet?
O suporte ao build do C++/WinRT (propriedades/destinos) está documentado no leiame do pacote NuGet Microsoft.Windows.CppWinRT.
Quais são os requisitos para a extensão do Visual Studio C++/WinRT (VSIX)?
Para a versão 1.0.190128.4 da extensão VSIX e posterior, consulte Visual Studio suporte para C++/WinRT. Para outras versões, consulte versões anteriores da extensão VSIX.
O que é uma classe de runtime?
Uma classe de runtime é um tipo que pode ser ativado e consumido por meio de interfaces COM modernas, normalmente entre limites executáveis. No entanto, uma classe de runtime também pode ser usada na unidade de compilação que a implementa. Você declara uma classe de runtime em IDL (Interface Definition Language) e pode implementá-la no C++ padrão usando C++/WinRT.
O que significa o tipo projetado e o tipo de implementação?
Se você estiver apenas consumindo uma classe do Windows Runtime (classe de tempo de execução), então estará lidando exclusivamente com tipos projetados. O C++/WinRT é uma projeção de linguagem; portanto, os tipos projetados fazem parte da interface do Windows Runtime que é projetada em C++ com o C++/WinRT. Para obter mais detalhes, consulte Consumir APIs com C++/WinRT.
O tipo de implementação contém a implementação de uma classe de runtime, portanto, ela só está disponível no projeto que implementa a classe de runtime. Quando você está trabalhando em um projeto que implementa classes de runtime (um projeto de componente Windows Runtime ou um projeto que usa a interface do usuário XAML), é importante estar confortável com a distinção entre o tipo de implementação de uma classe de runtime e o tipo projetado que representa a classe de runtime projetada em C++/WinRT. Para obter mais detalhes, consulte Criar APIs com C++/WinRT.
Preciso declarar um construtor na IDL da minha classe de runtime?
Somente se a classe de tempo de execução for projetada para ser consumida fora de sua unidade de compilação de implementação (é um componente do Tempo de Execução do Windows direcionado ao consumo geral pelos aplicativos cliente do Windows Runtime). Para obter detalhes completos sobre a finalidade e as consequências de declarar construtores em IDL, consulte construtores de classe runtime.
Por que o compilador está me dando um erro "C3779: consume_Something: função que retorna 'auto' não pode ser usada antes de ser definida"?
Você está usando um objeto Windows Runtime sem ter incluído primeiro o arquivo de cabeçalho do namespace correspondente. Inclua o cabeçalho com o nome do namespace da API e recompile. Para obter mais informações, consulte cabeçalhos de projeção C++/WinRT.
Por que o vinculador está me dando um erro de "LNK2019: símbolo externo não resolvido"?
Se o símbolo não resolvido for uma função livre do Windows Runtime, como RoInitialize, você precisará vincular explicitamente a biblioteca agregadora WindowsApp.lib ao seu projeto. A projeção de C++/WinRT depende de algumas dessas funções livres (não membro) e de pontos de entrada. Se você usar um dos modelos de projeto C++/WinRT Visual Studio Extension (VSIX) para o seu aplicativo, WindowsApp.lib será vinculado automaticamente. Caso contrário, você pode usar as configurações de link do projeto para incluí-lo ou fazê-lo no código-fonte.
#pragma comment(lib, "windowsapp")
É importante que você resolva os erros de vinculador que puder vinculando o WindowsApp.lib em vez de uma biblioteca alternativa de vínculo estático, caso contrário, seu aplicativo não passará nos testes do Kit de Certificação aplicativo do Windows usados pelo Visual Studio e pelo Microsoft Store para validar envios (o que significa que, consequentemente, não será possível que seu aplicativo seja ingerido com êxito no Microsoft Store).
Se o símbolo não resolvido for um construtor, talvez você tenha esquecido de incluir o arquivo de cabeçalho do namespace para a classe que está sendo construída. Inclua o cabeçalho com o nome do namespace da classe e recompile o projeto. Para obter mais informações, consulte cabeçalhos de projeção C++/WinRT.
Por que estou recebendo uma exceção de "classe não registrada"?
Nesse caso, o sintoma é que, ao construir uma classe de runtime ou acessar um membro estático, você vê uma exceção gerada no runtime com um valor HRESULT de REGDB_E_CLASSNOTREGISTERED.
Uma causa pode ser que o componente Windows Runtime não pode ser carregado. Verifique se o arquivo de metadados Windows Runtime do componente (.winmd) tem o mesmo nome que o binário do componente (o .dll), que também é o nome do projeto e o nome do namespace raiz. Certifique-se também de que os metadados Windows Runtime e o binário tenham sido copiados corectamente pelo processo de build para a pasta do Appx aplicativo de consumo. Confirme se o AppxManifest.xml do aplicativo de consumo (também na pasta Appx) contém um elemento <InProcessServer> que está declarando corretamente a classe ativável e o nome binário.
Construção uniforme Esse erro também pode acontecer se você tentar criar uma instância de uma classe de runtime implementada localmente por meio de qualquer um dos construtores do tipo projetado (diferente de seu construtor std::nullptr_t ). Para fazer isso, você precisará do recurso C++/WinRT 2.0 que geralmente é chamado de construção uniforme. Se você quiser aceitar esse recurso, para obter mais informações e exemplos de código, consulte Aceitar a construção uniforme e acesso direto à implementação.
Para obter uma maneira de instanciar suas classes de runtime implementadas localmente que não exigem construção uniforme, consulte controles XAML; associar a uma propriedade C++/WinRT.
Devo implementar Windows::Foundation::IClosable e, se for o caso, como?
Se você tiver uma classe de runtime que libera recursos em seu destruidor, e essa classe de runtime tiver sido projetada para ser consumida fora da unidade de compilação que a implementa (ou seja, um componente do Windows Runtime destinado ao uso geral por aplicativos cliente do Windows Runtime), recomendamos que você também implemente IClosable para dar suporte ao consumo da sua classe de runtime por linguagens que não têm finalização determinística. Certifique-se de que seus recursos sejam liberados se o destruidor, IClosable::Close, ou ambos forem chamados. IClosable::Close pode ser chamado um número arbitrário de vezes.
É necessário chamar IClosable::Close nas classes de runtime que eu consumo?
O IClosable existe para dar suporte a linguagens que não têm finalização determinística. Portanto, em geral, você não precisa chamar IClosable::Close do C++/WinRT. Mas considere essas exceções para essa regra geral.
- Há casos bem raros que envolvem corridas de desligamento ou adoções semifatais, em que é necessário chamar IClosable::Close. Se estiver usando tipos Windows.UI.Composition, por exemplo, você poderá se deparar com casos em que queira descartar objetos em uma sequência definida, como uma alternativa para permitir que a destruição do wrapper de C++/WinRT faça o trabalho para você.
- Se você não puder garantir que tem a última referência restante a um objeto (porque você o passou para outras APIs, que podem estar mantendo uma referência), chamar IClosable::Close é uma boa ideia.
- Em caso de dúvida, é seguro chamar IClosable::Close manualmente, em vez de esperar que o wrapper o chame na destruição.
Portanto, se você souber que tem a última referência, poderá deixar o destruidor de wrapper fazer o trabalho. Se você precisar fechar antes que a última referência desapareça, será necessário chamar Close. Para ser à prova de exceções, você deverá usar Fechar em um tipo RAII (aquisição de recursos é inicialização) (para que o fechamento aconteça no desenrolamento). C++/WinRT não tem um encapsulamento para unique_close, mas você pode criar o seu próprio.
Posso usar LLVM/Clang para compilar com C++/WinRT?
Não damos suporte à cadeia de ferramentas LLVM e Clang para C++/WinRT, mas fazemos uso dela internamente para validar a conformidade dos padrões do C++/WinRT. Por exemplo, se você quiser emular o que fazemos internamente, poderá tentar um experimento, como o descrito abaixo.
Vá para a Página de Download da LLVM, procure baixarbinários pré-criados da > e baixe o Clang para Windows (64 bits). Durante a instalação, opte por adicionar LLVM à variável do sistema PATH para que você possa invocá-la de um prompt de comando. Para fins deste experimento, você pode ignorar os erros "Falha ao localizar o diretório de conjuntos de ferramentas do MSBuild" e/ou erros de "falha na instalação da integração do MSVC", se você os vir. Há várias maneiras de invocar LLVM/Clang; o exemplo a seguir mostra apenas uma maneira.
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
Como o C++/WinRT usa recursos do padrão C++17, você precisará usar os sinalizadores do compilador necessários para obter esse suporte; esses sinalizadores diferem de um compilador para outro.
Visual Studio é a ferramenta de desenvolvimento que oferecemos suporte e recomendamos para C++/WinRT. Consulte Visual Studio suporte para C++/WinRT.
Por que a função de implementação gerada para uma propriedade somente leitura não tem o qualificador const?
Ao declarar uma propriedade somente leitura em MIDL 3.0, você pode esperar que a ferramenta cppwinrt.exe gere um função de implementação que seja qualificada para const (um função const trata o ponteiro this como const).
Certamente recomendamos usar const sempre que possível, mas a cppwinrt.exe própria ferramenta não tenta argumentar sobre quais funções de implementação podem ser concebivelmente const, e quais podem não ser. Você pode optar por tornar qualquer uma de suas funções de implementação const, como neste exemplo.
struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
winrt::hstring ToString() const
{
return L"MyStringable";
}
};
Você pode remover esse const qualificador no ToString caso decida que precisa alterar algum estado de objeto em sua implementação. Porém, transforme cada uma das suas funções de membro em const ou não const, não em ambos. Em outras palavras, não sobrecarrege uma função de implementação em const.
Além das suas funções de implementação, outro contexto em que const entra em cena é nas projeções de funções do Windows Runtime. Considere esse código.
int main()
{
winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
auto result{ s.ToString() };
}
Para a chamada a ToString acima, o comando Ir para Declaração no Visual Studio mostra que a projeção do Windows Runtime IStringable::ToString em C++/WinRT tem esta aparência.
winrt::hstring ToString() const;
As funções na projeção são const, independentemente de você optar por qualificar a implementação delas. Nos bastidores, a projeção faz uma chamada à ABI (interface binária de aplicativo), o que equivale a uma chamada feita por meio de um ponteiro para interface COM. O único estado com o qual o ToString projetado interage é o ponteiro da interface COM; e certamente não tem necessidade de modificar esse ponteiro, portanto, a função é const. Isso dá a você a garantia de que não alterará nada na referência IStringable por meio da qual você está fazendo a chamada, e garante que seja possível chamar ToString mesmo com uma referência const a um IStringable.
Entenda que esses exemplos de const são detalhes de implementação das projeções e implementações de C++/WinRT; eles constituem em limpeza de código para o seu benefício. Não existe nada como const no COM nem na ABI do Windows Runtime (para funções de membro).
Você tem alguma recomendação para diminuir o tamanho do código para binários C++/WinRT?
Ao trabalhar com objetos Windows Runtime, você deve evitar o padrão de codificação mostrado abaixo porque ele pode ter um impacto negativo em seu aplicativo, causando mais código binário do que o necessário para ser gerado.
anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();
No ambiente do Windows Runtime, o compilador não consegue armazenar em cache nem o valor de c() nem as interfaces de cada método chamado por meio de uma indireção ('.'). A menos que você intervenha, isso resulta em mais chamadas virtuais e sobrecarga de contagem de referência. O padrão acima poderia facilmente gerar o dobro de código do que estritamente necessário. Em vez disso, prefira o padrão mostrado abaixo onde puder. Ele gera muito menos código e também pode melhorar drasticamente o desempenho do tempo de execução.
auto a{ anobject.b().c() };
a.d();
a.e();
a.f();
O padrão recomendado mostrado acima aplica-se não apenas ao C++/WinRT, mas a todas as projeções de linguagem Windows Runtime.
Como transformar uma cadeia de caracteres em um tipo (para navegação, por exemplo)?
No final do exemplo de código de exibição de navegação (que está principalmente em C#), há um snippet de código C++/WinRT mostrando como fazer isso.
Como resolver ambiguidades com GetCurrentTime e/ou TRY?
O arquivo winrt/Windows.UI.Xaml.Media.Animation.h de cabeçalho declara um método chamado GetCurrentTime, enquanto windows.h (via winbase.h) define uma macro chamada GetCurrentTime. Quando os dois colidem, o compilador C++ produz "erro C4002: muitos argumentos para a invocação de macro semelhante à função GetCurrentTime".
Da mesma forma, winrt/Windows.Globalization.h declara um método chamado TRY, enquanto afx.h define uma macro chamada TRY. Quando eles entram em conflito, o compilador C++ produz o "Erro C2334: tokens inesperados antes de '{'; ignorando o corpo aparente da função".
Para corrigir um ou ambos os problemas, você pode fazer isso.
#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")
Como posso acelerar o carregamento de símbolos?
No Visual Studio, em Ferramentas>Opções>Depuração>Símbolos>, marque Carregar apenas os módulos especificados. Em seguida, você pode clicar com o botão direito do mouse nas DLLs da lista da pilha e carregar módulos individuais.
Note
Se este tópico não respondeu à sua pergunta, talvez você encontre ajuda visitando a comunidade de desenvolvedores do Visual Studio C++ ou usando a c++-winrt marca no Stack Overflow.