Respostas a perguntas que provavelmente terá sobre como criar e consumir APIs do Windows Runtime com C++/WinRT.
Important
Para notas de lançamento sobre C++/WinRT, consulte Notícias e alterações em C++/WinRT 2.0.
Note
Se a tua pergunta é sobre uma mensagem de erro que viste, então vê também o tópico Troubleshooting C++/WinRT .
Onde posso encontrar aplicações de exemplo em C++/WinRT?
Como posso redirecionar o meu projeto C++/WinRT para uma versão posterior do Windows SDK?
Porque é que o meu novo projeto não compila, agora que passei para C++/WinRT 2.0?
Para o conjunto completo de alterações (incluindo alterações de última hora), veja Notícias e alterações em C++/WinRT 2.0. Por exemplo, se estiver a usar um intervalo baseado for numa coleção do Windows Runtime, então agora terá de #include <winrt/Windows.Foundation.Collections.h>.
Porque é que o meu novo projeto não compila? Estou a usar o Visual Studio 2017 (versão 15.8.0 ou superior) e o SDK versão 17134
Se estiver a usar o Visual Studio 2017 (versão 15.8.0 ou superior) e tiver como destino o Windows SDK versão 10.0.17134.0 (Windows 10, versão 1803), então um projeto C++/WinRT recém-criado poderá não compilar com o erro "erro C3861: 'from_abi': identificador não encontrado", bem como com outros erros com origem em base.h. A solução passa por usar como destino uma versão posterior (mais conforme) do SDK do Windows, ou por definir a propriedade do projeto C/C++>Language>Conformance mode: No (além disso, se /permissive- aparecer na propriedade do projeto C/C++>Command Line, em Additional Options, elimine-o).
Como resolvo o erro de compilação? "O C++/WinRT VSIX já não oferece suporte para compilação de projetos. Adicione, por favor, uma referência ao projeto para o pacote NuGet Microsoft.Windows.CppWinRT"?
Instale o pacote NuGet Microsoft.Windows.CppWinRT no seu projeto. Para detalhes, veja Versões anteriores da extensão VSIX.
Como posso personalizar o suporte à compilação no pacote NuGet?
O suporte de compilação do C++/WinRT (props/targets) está documentado no ficheiro readme do pacote NuGet Microsoft.Windows.CppWinRT.
Quais são os requisitos para a Extensão Visual Studio (VSIX) C++/WinRT?
Para a versão 1.0.190128.4 da extensão VSIX e posteriores, veja suporte ao Visual Studio para C++/WinRT. Para outras versões, veja Versões anteriores da extensão VSIX.
O que é uma classe de tempo de execução?
Uma classe de runtime é um tipo que pode ser ativado e consumido através de interfaces COM modernas, tipicamente através de fronteiras executáveis. No entanto, uma classe de runtime também pode ser usada dentro da unidade de compilação que a implementa. Declaras uma classe de runtime na Interface Definition Language (IDL), e podes implementá-la em C++ padrão usando C++/WinRT.
O que significam o tipo projetado e o tipo de implementação?
Se estiveres apenas a utilizar uma classe do Windows Runtime (classe de runtime), então terás de lidar exclusivamente com tipos projetados. C++/WinRT é uma projeção de linguagem, por isso os tipos projetados fazem parte da superfície do Windows Runtime projetada em C++ com 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, por isso só está disponível no projeto que implementa a classe de runtime. Quando trabalhas num projeto que implementa classes de runtime (um projeto de componente do Windows Runtime, ou um projeto que usa interface XAML), é importante estares 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 mais detalhes, consulte APIs de autor com C++/WinRT.
Preciso de declarar um construtor no IDL da minha classe de runtime?
Só se a classe de runtime for concebida para ser consumida fora da sua unidade de compilação implementadora (é um componente Windows Runtime destinado ao consumo geral por aplicações cliente do Windows Runtime). Para detalhes completos sobre o propósito e as consequências de declarar construtor(es) em IDL, veja Construtores de classes em tempo de execução.
Porque é que o compilador me está a dar o erro "C3779: consume_Something: função que devolve 'auto' não pode ser usada antes de ser definida"?
Estás a usar um objeto do Windows Runtime sem antes ter incluído o ficheiro de cabeçalho correspondente do namespace. Inclua o cabeçalho nomeado pelo namespace da API e reconstrua. Para mais informações, consulte cabeçalhos de projeção C++/WinRT.
Porque é que o linker me está a dar um erro "LNK2019: Símbolo externo não resolvido"?
Se o símbolo não resolvido for uma função gratuita do Windows Runtime, como o RoInitialize, então terá de ligar explicitamente a biblioteca guarda-chuva WindowsApp.lib no seu projeto. A projeção C++/WinRT depende de algumas destas funções livres (não membros) e pontos de entrada. Se utilizar um dos modelos de projeto para a sua aplicação da C++/WinRT Visual Studio Extension (VSIX), então WindowsApp.lib é automaticamente associado. Caso contrário, podes usar as definições do link do projeto para o incluir, ou fazê-lo no código-fonte.
#pragma comment(lib, "windowsapp")
É importante que resolva todos os erros de linker que conseguir fazendo a ligação com WindowsApp.lib em vez de uma biblioteca alternativa para ligação estática; caso contrário, a sua aplicação não passará os testes do Aplicação do Windows Certification Kit utilizados pelo Visual Studio e pela Microsoft Store para validar submissões, o que significa que, consequentemente, não será possível que a sua aplicação seja processada com sucesso na Microsoft Store.
Se o símbolo não resolvido for um construtor, pode ter-se esquecido de incluir o ficheiro de cabeçalho namespace para a classe que está a ser construída. Incluir o cabeçalho nomeado pelo namespace da classe e reconstruir. Para mais informações, consulte cabeçalhos de projeção C++/WinRT.
Porque é que estou a receber uma exceção de "classe não registada"?
Neste caso, o sintoma é que — ao construir uma classe em tempo de execução ou aceder a um membro estático — vê-se uma exceção lançada em tempo de execução com um valor HRESULT de REGDB_E_CLASSNOTREGISTERED.
Uma das causas pode ser que o seu componente do Windows Runtime não consiga ser carregado. Certifique-se de que o ficheiro de metadados Windows Runtime do componente (.winmd) tem o mesmo nome do binário do componente (o .dll), que é também o nome do projeto e do namespace raiz. Certifique-se também de que os metadados do Windows Runtime e o binário foram corretamente copiados pelo processo de compilação para a pasta da Appx aplicação que o consome. E confirme que a aplicação cliente AppxManifest.xml (também na pasta Appx) contém um elemento <InProcessServer> que declara corretamente a classe que pode ser ativada e o nome do binário.
Construção uniforme Este erro também pode acontecer se tentar instanciar uma classe de runtime localmente implementada através de qualquer um dos construtores do tipo projetado (exceto o seu construtor std::nullptr_t ). Para isso, vais precisar da funcionalidade C++/WinRT 2.0 que é frequentemente chamada de construção uniforme. Se quiseres aderir a essa funcionalidade, para mais informações e exemplos de código, vê Optar para construção uniforme e acesso direto à implementação.
Para uma forma de instanciar as suas classes de runtime implementadas localmente que não requer construção uniforme, veja controlos XAML; vincular a uma propriedade C++/WinRT.
Devo implementar o Windows::Foundation::IClosable e, se sim, como?
Se tiver uma classe de runtime que liberta recursos no seu destruidor, e essa classe de runtime for concebida para ser consumida fora da sua unidade de compilação de implementação (é um componente do Windows Runtime destinado ao consumo geral por aplicações cliente do Windows Runtime), então recomendamos que também implemente o IClosable para suportar o consumo da sua classe de execução por linguagens que carecem de finalização determinística. Certifique-se de que os seus recursos são libertados, quer sejam chamados o destrutor, IClosable::Close, quer ambos. IClosable::Close pode ser invocado um número arbitrário de vezes.
Preciso de chamar IClosable::Close nas classes de runtime que consumo?
O IClosable existe para suportar linguagens que carecem de finalização determinística. Portanto, em geral, não precisas de chamar IClosable::Close a partir de C++/WinRT. Mas considere estas exceções a essa regra geral.
- Existem casos muito raros envolvendo raças de shutdown ou abraços semi-mortais, em que é preciso chamar IClosable::Close. Se estiver a usar tipos de Windows.UI.Composition, por exemplo, poderá deparar-se com casos em que queira eliminar objetos numa sequência específica, em alternativa a deixar que a destruição do wrapper de C++/WinRT trate disso por si.
- Se não conseguires garantir que tens a última referência restante a um objeto (porque a passaste para outras APIs, que podem estar a manter uma referência), então chamar IClosable::Close é uma boa ideia.
- Em caso de dúvida, é seguro chamar manualmente IClosable::Close, em vez de esperar que o wrapper o chame ao ser destruído.
Portanto, se souber que tem a última referência, pode deixar o destruidor do invólucro fazer o trabalho. Se precisares de fechar antes que a última referência desapareça, tens de chamar Close. Para estar à prova de exceções, deve encapsular Close num tipo de aquisição de recursos é inicialização (RAII) (para que o fecho ocorra durante o desenrolar da pilha). O C++/WinRT não tem um wrapper unique_close , mas podes criar o teu próprio.
Posso usar LLVM/Clang para compilar com C++/WinRT?
Não suportamos a cadeia de ferramentas LLVM e Clang para C++/WinRT, mas utilizamo-la internamente para validar a conformidade com os padrões C++/WinRT. Por exemplo, se quiser emular o que fazemos internamente, pode tentar uma experiência como a descrita abaixo.
Vai à página de download do LLVM, procura por Download LLVM 6.0.0>Pre-Built Binaries e descarrega o Clang para Windows (64 bits). Durante a instalação, opte por adicionar LLVM à variável do sistema PATH para que possa invocá-lo a partir de um prompt de comandos. Para efeitos desta experiência, pode ignorar quaisquer mensagens de erro "Failed to find MSBuild toolsets directory" e/ou "MSVC integration install failed", caso surjam. Existem várias formas de invocar LLVM/Clang; O exemplo abaixo mostra apenas uma direção.
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 utiliza funcionalidades do padrão C++17, terá de usar os flags do compilador necessários para obter esse suporte; tais flags diferem de um compilador para outro.
O Visual Studio é a ferramenta de desenvolvimento que suportamos e recomendamos para C++/WinRT. Veja o suporte do Visual Studio para C++/WinRT.
Porque é que a função de implementação gerada para uma propriedade de apenas leitura não tem o qualificador const?
Quando declara uma propriedade só de leitura em MIDL 3.0, poderá esperar que a ferramenta cppwinrt.exe gere uma função de implementação com o qualificador const (uma função const trata o ponteiro this como const).
Recomendamos certamente usar const sempre que possível, mas a cppwinrt.exe própria ferramenta não tenta raciocinar sobre quais funções de implementação poderiam ser const e quais não poderiam ser. Pode optar por declarar qualquer uma das suas funções de implementação como const, como neste exemplo.
struct MyStringable : winrt::implements<MyStringable, winrt::Windows::Foundation::IStringable>
{
winrt::hstring ToString() const
{
return L"MyStringable";
}
};
Podes remover esse const qualificador no ToString caso decidas que precisas de alterar algum estado do objeto na sua implementação. Mas garante que cada uma das tuas funções-membro seja const ou não const, mas não ambas. Ou seja, não sobrecarregue uma função de implementação em const.
Para além das tuas funções de implementação, outro local onde o const entra em cena é nas projeções de funções do Windows Runtime. Considera este código.
int main()
{
winrt::Windows::Foundation::IStringable s{ winrt::make<MyStringable>() };
auto result{ s.ToString() };
}
Para a chamada ao ToString acima, o comando Go To Declaration no Visual Studio mostra que a projeção do Windows Runtime IStringable::ToString para C++/WinRT tem este aspeto.
winrt::hstring ToString() const;
As funções na projeção são const independentemente de como escolhas qualificar a tua implementação delas. Nos bastidores, o mecanismo de projeção faz uma chamada à interface binária da aplicação (ABI), o que equivale a uma chamada por meio de um ponteiro para uma interface COM. O único estado com que o ToString projetado interage é aquele ponteiro de interface COM; e certamente não precisa de modificar esse ponteiro, por isso a função é const. Isto dá-te a certeza de que não vai alterar nada na referência IStringable por onde estás a chamar, e garante que podes chamar ToString mesmo com uma referência const a um IStringable.
Tenha em conta que estes exemplos de const são detalhes de implementação das projeções e implementações do C++/WinRT; representam boas práticas de código para seu benefício. Não existe algo como const no COM nem no Windows Runtime ABI (para funções membros).
Tens alguma recomendação para diminuir o tamanho do código para binários C++/WinRT?
Ao trabalhar com objetos do Windows Runtime, deve evitar o padrão de programação mostrado abaixo, pois pode ter um impacto negativo na sua aplicação ao causar a geração de mais código binário do que o necessário.
anobject.b().c().d();
anobject.b().c().e();
anobject.b().c().f();
No mundo Windows Runtime, o compilador não consegue armazenar em cache nem o valor de c() nem as interfaces de cada método chamado através de uma indireção ('.'). A menos que intervenha, isso resulta em mais chamadas virtuais e sobrecarga de contagem de referências. O padrão acima poderia facilmente gerar o dobro de código do que o estritamente necessário. Em vez disso, prefira o padrão mostrado abaixo sempre que puder. Gera muito menos código e também pode melhorar drasticamente o desempenho em 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 só ao C++/WinRT, mas a todas as projeções da linguagem Windows Runtime.
Como é que converto uma cadeia de caracteres num tipo (para navegação, por exemplo)?
No final do exemplo do código da vista de navegação (que está maioritariamente em C#), há um excerto de código em C++/WinRT a mostrar como fazer isto.
Como resolvo ambiguidades com o GetCurrentTime e/ou TRY?
O ficheiro 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: Demasiados argumentos para a invocação de macro tipo função GetCurrentTime".
De forma semelhante, winrt/Windows.Globalization.h declara um método chamado TRY, enquanto afx.h define uma macro chamada TRY. Quando estes colidem, o compilador C++ produz "erro C2334: token(s) inesperado(s) antecedendo '{'; saltando o corpo de função aparente".
Para resolver um ou ambos os problemas, pode fazer isto.
#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 é que acelero o carregamento dos símbolos?
No Visual Studio, em Ferramentas>Opções>Depuração>Símbolos>, selecione Carregar apenas os módulos especificados. Pode então clicar com o botão direito do rato nas DLLs na lista da pilha e carregar módulos individuais.
Note
Se este tópico não respondeu à sua pergunta, pode encontrar ajuda visitando a comunidade de programadores C++ do Visual Studio, ou usando a c++-winrt tag no Stack Overflow.