Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este tópico mostra como consumir APIs de C++/WinRT, sejam elas parte do Windows, implementadas por um fornecedor de componentes de terceiros ou implementadas por você mesmo.
Importante
Para que os exemplos de código neste tópico sejam curtos e fáceis de experimentar, você pode reproduzi-los criando um novo projeto de aplicativo de console do Windows (C++/WinRT) e copiando código. No entanto, você não pode consumir tipos arbitrários personalizados (de terceiros) do Windows Runtime de um aplicativo não empacotado dessa forma. Você pode consumir somente tipos do Windows dessa maneira.
Para consumir tipos personalizados (de terceiros) do Windows Runtime de um aplicativo de console, você precisará fornecer ao aplicativo uma identidade de pacote para que ele possa resolver o registro dos tipos personalizados consumidos. Para obter mais informações, consulte Projeto de Empacotamento de Aplicativos do Windows.
Como alternativa, crie um novo projeto a partir dos modelos de projeto Aplicativo em Branco (C++/WinRT), Aplicativo Principal (C++/WinRT), ou Componente do Windows Runtime (C++/WinRT). Esses tipos de aplicativo já têm uma identidade de pacote.
Se a API estiver em um namespace do Windows
Esse é o caso mais comum em que você consumirá uma API do Windows Runtime. Para cada tipo em um namespace do Windows definido em metadados, o C++/WinRT define um equivalente C++ amigável (chamado de tipo projetado
Aqui está um exemplo de código simples. Se você quiser copiar e colar os exemplos de código a seguir diretamente no arquivo de código-fonte principal de um projeto Aplicativo de Console do Windows (C++/WinRT), primeiro, configure para Não Usar Cabeçalhos Pré-compilados nas propriedades do projeto.
// main.cpp
#include <winrt/Windows.Foundation.h>
using namespace winrt;
using namespace Windows::Foundation;
int main()
{
winrt::init_apartment();
Uri contosoUri{ L"http://www.contoso.com" };
Uri combinedUri = contosoUri.CombineUri(L"products");
}
O cabeçalho winrt/Windows.Foundation.h
incluído faz parte do SDK, encontrado dentro da pasta %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt\
. Os cabeçalhos nessa pasta contêm tipos de namespace do Windows projetados em C++/WinRT. Neste exemplo, winrt/Windows.Foundation.h
contém winrt::Windows::Foundation::Uri, que é o tipo projetado para a classe de tempo de execução Windows::Foundation::Uri.
Dica
Sempre que você quiser usar um tipo de um namespace do Windows, inclua o cabeçalho C++/WinRT correspondente a esse namespace. As diretivas using namespace
são opcionais, mas convenientes.
No exemplo de código acima, após a inicialização do C++/WinRT, alocamos na pilha um valor do tipo winrt::Windows::Foundation::Uri por meio de um de seus construtores documentados publicamente (Uri(String), neste exemplo). Para isso, no caso de uso mais comum, normalmente é tudo o que você precisa fazer. Depois de ter um valor de tipo projetado em C++/WinRT, você poderá tratá-lo como se fosse uma instância do tipo real do Windows Runtime, já que ele tem todos os mesmos membros.
Na verdade, esse valor projetado é um proxy; é essencialmente apenas um ponteiro inteligente para um objeto de backup. As chamadas dos construtores do valor projetado RoActivateInstance para criar uma instância da classe de suporte do Windows Runtime (Windows.Foundation.Uri, nesse caso) e armazenar a interface padrão desse objeto dentro do novo valor projetado. Conforme ilustrado abaixo, suas chamadas para os membros do valor projetado de fato delegam, por meio do ponteiro inteligente, para o objeto subjacente, onde ocorrem alterações de estado.
Quando o valor contosoUri
sai do escopo, ele se destrói e libera sua referência à interface padrão. Se essa referência for a última referência ao objeto de suporte Windows Runtime Windows.Foundation.Uri, o objeto de suporte também é destruído.
Dica
Um tipo projetado é um wrapper sobre um tipo de Tempo de Execução do Windows para fins de consumir suas APIs. Por exemplo, uma interface projetada é um envoltório sobre uma interface do Windows Runtime.
Cabeçalhos de projeção do C++/WinRT
Para consumir APIs do namespace do Windows com C++/WinRT, inclua arquivos de cabeçalho da pasta %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt
. Você deve incluir os cabeçalhos correspondentes a cada namespace usado.
Por exemplo, para o namespace Windows::Security::Cryptography::Certificates , as definições de tipo C++/WinRT equivalentes residem em winrt/Windows.Security.Cryptography.Certificates.h
. A inclusão desse cabeçalho fornece acesso a todos os tipos no namespace Windows::Security::Cryptography::Certificates .
Às vezes, um cabeçalho de namespace incluirá partes de cabeçalhos de namespace relacionados, mas você não deve contar com esses detalhes de implementação. Inclua explicitamente os cabeçalhos para os namespaces que você usa.
Por exemplo, o método Certificate::GetCertificateBlob retorna uma interface Windows::Storage::Streams::IBuffer .
Antes de chamar o método Certificate::GetCertificateBlob, você deve incluir o arquivo de cabeçalho do namespace winrt/Windows.Storage.Streams.h
para garantir que você possa receber e operar com o Windows::Storage::Streams::IBuffer retornado.
Esquecer de incluir os cabeçalhos de namespace necessários antes de usar tipos nesse namespace é uma fonte comum de erros de build.
Acessando membros por meio do objeto, por meio de uma interface ou por meio da ABI
Com a projeção C++/WinRT, a representação de uma classe do Windows Runtime em tempo de execução não passa das interfaces ABI subjacentes. Mas, para sua conveniência, você pode programar utilizando classes de acordo com a intenção do autor. Por exemplo, você pode chamar o método
WINRT_ASSERT
é uma definição de macro e se expande para _ASSERTE.
Uri contosoUri{ L"http://www.contoso.com" };
WINRT_ASSERT(contosoUri.ToString() == L"http://www.contoso.com/"); // QueryInterface is called at this point.
Essa conveniência é obtida por meio de uma consulta para a interface apropriada. Mas você está sempre no controle. Você pode optar por sacrificar um pouco dessa conveniência em troca de um pouco mais de desempenho, obtendo a interface IStringable por você mesmo e usando-a diretamente. No exemplo de código abaixo, você obtém um ponteiro real para a interface IStringable durante a execução (por meio de uma consulta única). Depois disso, sua chamada para ToString é direta e evita qualquer chamada adicional para QueryInterface.
...
IStringable stringable = contosoUri; // One-off QueryInterface.
WINRT_ASSERT(stringable.ToString() == L"http://www.contoso.com/");
Você poderá escolher essa técnica se souber que chamará vários métodos na mesma interface.
Aliás, se você quiser acessar membros no nível da ABI, você pode. O exemplo de código abaixo mostra como, e há mais detalhes e exemplos de código na sobre a interoperabilidade entre C++/WinRT e a ABI.
#include <Windows.Foundation.h>
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
using namespace winrt::Windows::Foundation;
int main()
{
winrt::init_apartment();
Uri contosoUri{ L"http://www.contoso.com" };
int port{ contosoUri.Port() }; // Access the Port "property" accessor via C++/WinRT.
winrt::com_ptr<ABI::Windows::Foundation::IUriRuntimeClass> abiUri{
contosoUri.as<ABI::Windows::Foundation::IUriRuntimeClass>() };
HRESULT hr = abiUri->get_Port(&port); // Access the get_Port ABI function.
}
Inicialização atrasada
No C++/WinRT, cada tipo projetado tem um construtor C++/WinRT especial std::nullptr_t. Com exceção desse, todos os construtores de tipo projetado, incluindo o construtor padrão, resultam na criação de um objeto subjacente do Windows Runtime e fornecem um smart pointer para ele. Portanto, essa regra se aplica em qualquer lugar em que o construtor padrão seja usado, como variáveis locais não inicializadas, variáveis globais não inicializadas e variáveis de membro não inicializadas.
Se, por outro lado, você quiser construir uma variável de um tipo projetado sem que ele, por sua vez, construa um objeto do Windows Runtime de suporte (para que você possa atrasar esse trabalho até mais tarde), então você pode fazer isso. Declare sua variável ou campo usando esse construtor C++/WinRT std::nullptr_t especial (que a projeção C++/WinRT injeta em cada classe de runtime). Usamos esse construtor especial com m_gamerPicBuffer no exemplo de código abaixo.
#include <winrt/Windows.Storage.Streams.h>
using namespace winrt::Windows::Storage::Streams;
#define MAX_IMAGE_SIZE 1024
struct Sample
{
void DelayedInit()
{
// Allocate the actual buffer.
m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
}
private:
Buffer m_gamerPicBuffer{ nullptr };
};
int main()
{
winrt::init_apartment();
Sample s;
// ...
s.DelayedInit();
}
Todos os construtores no tipo projetado exceto construtor std::nullptr_t fazem com que um objeto do Windows Runtime de backup seja criado. O construtor std::nullptr_t é essencialmente um no-op. Ele espera que o objeto projetado seja inicializado em um momento subsequente. Portanto, se uma classe de runtime tem um construtor padrão ou não, você pode usar essa técnica para inicialização atrasada eficiente.
Essa consideração afeta outros locais em que você está invocando o construtor padrão, como em vetores e mapas. Considere este exemplo de código, para o qual você precisará de um projeto do aplicativo em branco (C++/WinRT).
std::map<int, TextBlock> lookup;
lookup[2] = value;
A atribuição cria um novo TextBlocke, em seguida, imediatamente o sobrescreve com value
. Aqui está o remédio.
std::map<int, TextBlock> lookup;
lookup.insert_or_assign(2, value);
Veja também como o construtor padrão afeta as coleções.
Não inicialize tardiamente por engano
Tenha cuidado para não invocar o construtor std::nullptr_t por engano. O compilador tem prioridade sobre os construtores de fábrica na resolução de conflitos. Por exemplo, considere essas duas definições de classe de runtime.
// GiftBox.idl
runtimeclass GiftBox
{
GiftBox();
}
// Gift.idl
runtimeclass Gift
{
Gift(GiftBox giftBox); // You can create a gift inside a box.
}
Digamos que queremos construir um Gift que não esteja dentro de uma caixa (um Gift que seja construído com um GiftBoxnão inicializado). Primeiro, vamos examinar a maneira errada de fazer isso. Sabemos que há um construtor Gift que aceita um GiftBox. Mas se estivermos tentados a passar um
// These are *not* what you intended. Doing it in one of these two ways
// actually *doesn't* create the intended backing Windows Runtime Gift object;
// only an empty smart pointer.
Gift gift{ nullptr };
auto gift{ Gift(nullptr) };
O que você obtém aqui é um Presentenão inicializado. Você não recebe um presente
// Doing it in one of these two ways creates an initialized
// Gift with an uninitialized GiftBox.
Gift gift{ GiftBox{ nullptr } };
auto gift{ Gift(GiftBox{ nullptr }) };
No exemplo incorreto, passar um literal nullptr
resulta em preferência pelo construtor de inicialização tardia. Para resolver em favor do construtor da fábrica, o tipo do parâmetro deve ser um GiftBox . Você ainda tem a opção de passar um giftBox explicitamente com atraso, conforme mostrado no exemplo correto.
Este próximo exemplo também é correto, pois o parâmetro tem tipo GiftBox e não std::nullptr_t.
GiftBox giftBox{ nullptr };
Gift gift{ giftBox }; // Calls factory constructor.
É só quando você passa um literal nullptr
que a ambiguidade surge.
Não copie a construção por engano.
Essa cautela é semelhante à descrita na seção Não atrasar a inicialização por engano acima.
Além do construtor de inicialização atrasada, a projeção C++/WinRT também injeta um construtor de cópia em cada classe de tempo de execução. É um construtor de parâmetro único que aceita o mesmo tipo que o objeto que está sendo construído. O ponteiro inteligente resultante aponta para o mesmo objeto de tempo de execução do Windows ao qual o parâmetro do construtor aponta. O resultado são dois objetos de ponteiro inteligente apontando para o mesmo objeto subjacente.
Aqui está uma definição de classe de runtime que usaremos nos exemplos de código.
// GiftBox.idl
runtimeclass GiftBox
{
GiftBox(GiftBox biggerBox); // You can place a box inside a bigger box.
}
Digamos que queremos construir uma Caixa de Presente dentro de uma Caixa de Presente maior .
GiftBox bigBox{ ... };
// These are *not* what you intended. Doing it in one of these two ways
// copies bigBox's backing-object-pointer into smallBox.
// The result is that smallBox == bigBox.
GiftBox smallBox{ bigBox };
auto smallBox{ GiftBox(bigBox) };
A maneira correta de fazer isso é chamar a fábrica de ativação explicitamente.
GiftBox bigBox{ ... };
// These two ways call the activation factory explicitly.
GiftBox smallBox{
winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
auto smallBox{
winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
Se a API estiver implementada em um componente do Windows Runtime
Esta seção se aplica se você mesmo criou o componente ou se ele veio de um fornecedor.
Observação
Para obter informações sobre como instalar e usar a Extensão C++/WinRT para Visual Studio (VSIX) e o pacote NuGet (que juntos fornecem suporte ao modelo de projeto e ao build), consulte suporte do Visual Studio para C++/WinRT.
Em seu projeto de aplicativo, faça referência ao arquivo de metadados do Windows Runtime do componente Windows Runtime (.winmd
) e construa. Durante a compilação, a ferramenta cppwinrt.exe
gera uma biblioteca C++ padrão que descreve totalmente — ou projetos— a superfície da API para o componente. Em outras palavras, a biblioteca gerada contém os tipos projetados para o componente.
Em seguida, assim como para um tipo de namespace do Windows, você inclui um cabeçalho e constrói o tipo projetado por meio de um de seus construtores. O código de inicialização do projeto de aplicativo registra a classe de runtime e o construtor do tipo projetado chama RoActivateInstance para ativar a classe de runtime do componente referenciado.
#include <winrt/ThermometerWRC.h>
struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
ThermometerWRC::Thermometer thermometer;
...
};
Para obter mais detalhes, código e um passo a passo do consumo de APIs implementadas em um componente do Windows Runtime, consulte componentes do Windows Runtime com eventos C++/WinRT e Author no C++/WinRT.
Se a API for implementada no projeto de consumo
O exemplo de código nesta seção é obtido do tópico controles XAML; associar a uma propriedade C++/WinRT. Consulte esse tópico para obter mais detalhes, código e um passo a passo do consumo de uma classe de runtime implementada no mesmo projeto que o consome.
Um tipo consumido da interface do usuário XAML deve ser uma classe de runtime, mesmo que esteja no mesmo projeto que o XAML. Para esse cenário, você gera um tipo projetado com base nos metadados do Windows Runtime da classe de runtime (.winmd
). Novamente, você inclui um cabeçalho, mas tem uma opção entre as maneiras C++/WinRT versão 1.0 ou versão 2.0 de construir a instância da classe de runtime. O método da versão 1.0 usa winrt::make, e o método da versão 2.0 é conhecido como construção uniforme . Vamos examinar cada um por sua vez.
Construir usando winrt::make
Vamos começar com o método padrão (C++/WinRT versão 1.0), pois é uma boa ideia estar pelo menos familiarizado com esse padrão. Você cria o tipo projetado através de seu construtor std::nullptr_t. Esse construtor não executa nenhuma inicialização, portanto, você deve atribuir um valor à instância por meio da função auxiliar winrt::make , passando os argumentos de construtor necessários. Uma classe de tempo de execução implementada no mesmo projeto que o código que consome não precisa ser registrada nem instanciada por meio da ativação do Windows Runtime/COM.
Veja os controles XAML ; associe a uma propriedade C++/WinRT para um passo a passo completo. Esta seção mostra os extratos desse passo a passo.
// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
BookstoreViewModel MainViewModel{ get; };
}
}
// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
private:
Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
...
// MainPage.cpp
...
#include "BookstoreViewModel.h"
MainPage::MainPage()
{
m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
...
}
Construção uniforme
Com o C++/WinRT versão 2.0 e posteriores, há uma forma otimizada de construção disponível para você, conhecida como construção uniforme (consulte Notícias e alterações no C++/WinRT 2.0).
Veja os controles XAML ; associe a uma propriedade C++/WinRT para um passo a passo completo. Esta seção mostra os extratos desse passo a passo.
Para usar a construção uniforme em vez de winrt::make, você precisará de uma fábrica de ativação. Uma boa maneira de gerar um objeto é adicionar um construtor à sua IDL.
// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
{
MainPage();
BookstoreViewModel MainViewModel{ get; };
}
}
Em seguida, em MainPage.h
declarar e inicializar m_mainViewModel em apenas uma etapa, conforme mostrado abaixo.
// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
private:
Bookstore::BookstoreViewModel m_mainViewModel;
...
};
}
...
E então, no construtor MainPage em MainPage.cpp
, não há necessidade do código m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
.
Para obter mais informações sobre construção uniforme e exemplos de código, consulte Optar pela construção uniforme e acesso direto à implementação.
Instanciando e retornando tipos e interfaces projetados
Aqui está um exemplo de como os tipos e interfaces projetados podem se parecer em seu projeto de consumo. Lembre-se de que um tipo projetado (como o deste exemplo) é gerado por ferramentas e não é algo que você mesmo criaria.
struct MyRuntimeClass : MyProject::IMyRuntimeClass, impl::require<MyRuntimeClass,
Windows::Foundation::IStringable, Windows::Foundation::IClosable>
MyRuntimeClass é um tipo projetado; as interfaces projetadas incluem IMyRuntimeClass, IStringable e IClosable. Este tópico mostrou as diferentes maneiras de instanciar um tipo projetado. Aqui está um lembrete e resumo, usando MyRuntimeClass como exemplo.
// The runtime class is implemented in another compilation unit (it's either a Windows API,
// or it's implemented in a second- or third-party component).
MyProject::MyRuntimeClass myrc1;
// The runtime class is implemented in the same compilation unit.
MyProject::MyRuntimeClass myrc2{ nullptr };
myrc2 = winrt::make<MyProject::implementation::MyRuntimeClass>();
- Você pode acessar os membros de todas as interfaces de um tipo projetado.
- Você pode retornar um tipo projetado para um chamador.
- Os tipos e interfaces projetados derivam de winrt::Windows::Foundation::IUnknown. Portanto, você pode chamar IUnknown::as em um tipo ou interface projetada para consultar outras interfaces projetadas, que você também pode usar ou retornar para quem fez a chamada. A função como membro funciona como QueryInterface.
void f(MyProject::MyRuntimeClass const& myrc)
{
myrc.ToString();
myrc.Close();
IClosable iclosable = myrc.as<IClosable>();
iclosable.Close();
}
Fábricas de ativação
A maneira conveniente e direta de criar um objeto C++/WinRT é a seguinte.
using namespace winrt::Windows::Globalization::NumberFormatting;
...
CurrencyFormatter currency{ L"USD" };
Mas pode haver momentos em que você deseja criar a fábrica de ativação por conta própria e, em seguida, criar objetos a partir dela à sua conveniência. Aqui estão alguns exemplos mostrando como usar o modelo de função winrt::get_activation_factory.
using namespace winrt::Windows::Globalization::NumberFormatting;
...
auto factory = winrt::get_activation_factory<CurrencyFormatter, ICurrencyFormatterFactory>();
CurrencyFormatter currency = factory.CreateCurrencyFormatterCode(L"USD");
using namespace winrt::Windows::Foundation;
...
auto factory = winrt::get_activation_factory<Uri, IUriRuntimeClassFactory>();
Uri uri = factory.CreateUri(L"http://www.contoso.com");
As classes nos dois exemplos acima são tipos de um namespace do Windows. Neste próximo exemplo, ThermometerWRC::Thermometer é um tipo personalizado implementado em um componente do Windows Runtime.
auto factory = winrt::get_activation_factory<ThermometerWRC::Thermometer>();
ThermometerWRC::Thermometer thermometer = factory.ActivateInstance<ThermometerWRC::Thermometer>();
Ambiguidades de membro/tipo
Quando uma função membro tem o mesmo nome que um tipo, há ambiguidade. As regras para pesquisa de nome não qualificado do C++ em funções de membro fazem com que ela pesquise a classe antes de pesquisar em namespaces. A falha de substituição não é um erro; a regra (SFINAE) não se aplica (ela se aplica durante a resolução de sobrecarga de funções template). Portanto, se o nome dentro da classe não fizer sentido, o compilador não continuará procurando uma correspondência melhor, ele simplesmente relatará um erro.
struct MyPage : Page
{
void DoWork()
{
// This doesn't compile. You get the error
// "'winrt::Windows::Foundation::IUnknown::as':
// no matching overloaded function found".
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Style>() };
}
}
Acima, o compilador considera que você está passando FrameworkElement.Style() (que, em C++/WinRT, é uma função membro) como o parâmetro de modelo para IUnknown::as. A solução é forçar o nome Style
a ser interpretado como o tipo Windows::UI::Xaml::Style.
struct MyPage : Page
{
void DoWork()
{
// One option is to fully-qualify it.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Windows::UI::Xaml::Style>() };
// Another is to force it to be interpreted as a struct name.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<struct Style>() };
// If you have "using namespace Windows::UI;", then this is sufficient.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Xaml::Style>() };
// Or you can force it to be resolved in the global namespace (into which
// you imported the Windows::UI::Xaml namespace when you did
// "using namespace Windows::UI::Xaml;".
auto style = Application::Current().Resources().
Lookup(L"MyStyle").as<::Style>();
}
}
A pesquisa de nome não qualificada tem uma exceção especial no caso de o nome ser seguido por ::
; nesse caso, ignora funções, variáveis e valores de enum. Isso permite que você faça coisas assim.
struct MyPage : Page
{
void DoSomething()
{
Visibility(Visibility::Collapsed); // No ambiguity here (special exception).
}
}
A chamada para Visibility()
se resolve para o nome da função membro UIElement.Visibility. Mas o parâmetro Visibility::Collapsed
segue a palavra Visibility
com ::
e, portanto, o nome do método é ignorado e o compilador localiza a classe enum.
APIs importantes
- função QueryInterface
- Função RoActivateInstance
- classe Windows::Foundation::Uri
- modelo de função winrt::get_activation_factory
- modelo de função do winrt::make
- struct winrt::Windows::Foundation::IUnknown