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 converter entre objetos ABI (interface binária do aplicativo SDK) e C++/WinRT . Você pode usar essas técnicas para interoperabilidade entre código que usa essas duas maneiras de programação com o Windows Runtime ou pode usá-las à medida que move gradualmente seu código da ABI para C++/WinRT.
Em geral, O C++/WinRT expõe os tipos de ABI como void*, para que você não precise incluir arquivos de cabeçalho de plataforma.
Note
Nos exemplos de código, usamos reinterpret_cast (em vez de static_cast) para sinalizar que se trata de conversões inerentemente inseguras.
Qual é o Windows Runtime ABI e quais são os tipos de ABI?
Uma classe Windows Runtime (classe de runtime) é realmente uma abstração. Essa abstração define uma interface binária (a Interface Binária de Aplicativo ou ABI) que permite que várias linguagens de programação interajam com um objeto. Independentemente da linguagem de programação, a interação de código do cliente com um objeto Windows Runtime ocorre no nível mais baixo, com construções de linguagem de cliente traduzidas em chamadas para a ABI do objeto.
Os cabeçalhos do SDK Windows na pasta "%WindowsSdkDir%Include\10.0.17134.0\winrt" (ajuste o número de versão do SDK para seu caso, se necessário), são os arquivos de cabeçalho da ABI Windows Runtime. Eles foram produzidos pelo compilador MIDL. Aqui está um exemplo de como incluir um desses cabeçalhos.
#include <windows.foundation.h>
E aqui está um exemplo simplificado de um dos tipos de ABI que você encontrará nesse cabeçalho específico do SDK. Observe o namespace da ABI; Windows::Foundation e todos os outros namespaces Windows são declarados pelos cabeçalhos do SDK dentro do namespace da ABI.
namespace ABI::Windows::Foundation
{
IUriRuntimeClass : public IInspectable
{
public:
/* [propget] */ virtual HRESULT STDMETHODCALLTYPE get_AbsoluteUri(/* [retval, out] */__RPC__deref_out_opt HSTRING * value) = 0;
...
}
}
IUriRuntimeClass é uma interface COM. Mas, mais do que isso, já que sua base é IInspectable, iUriRuntimeClass é uma interface Windows Runtime. Observe o tipo de retorno HRESULT, em vez de lançar exceções. E o uso de artefatos como o handle HSTRING (é uma boa prática redefinir esse handle para nullptr quando terminar de usá-lo). Isso fornece um gostinho da aparência do Windows Runtime no nível binário do aplicativo; em outras palavras, no nível de programação COM.
O Windows Runtime é baseado em APIs COM (Component Object Model). Você pode acessar a Windows Runtime dessa forma ou acessá-la por meio de projeções de linguagem. Uma projeção oculta os detalhes do COM e fornece uma experiência de programação mais natural para um determinado idioma.
Por exemplo, se você procurar na pasta "%WindowsSdkDir%Include\10.0.17134.0\cppwinrt\winrt" (novamente, ajuste o número de versão do SDK para seu caso, se necessário), então você encontrará os cabeçalhos de projeção da linguagem C++/WinRT. Há um cabeçalho para cada namespace Windows, assim como há um cabeçalho ABI por namespace Windows. Aqui está um exemplo de como incluir um dos cabeçalhos C++/WinRT.
#include <winrt/Windows.Foundation.h>
E, a partir desse cabeçalho, aqui está a versão simplificada do equivalente em C++/WinRT desse tipo ABI que acabamos de ver.
namespace winrt::Windows::Foundation
{
struct Uri : IUriRuntimeClass, ...
{
winrt::hstring AbsoluteUri() const { ... }
...
};
}
A interface aqui é moderna, padrão C++. Ele acaba com HRESULTs (C++/WinRT gera exceções, se necessário). E a função acessador retorna um objeto de cadeia de caracteres simples, que é limpo no final de seu escopo.
Este tópico é para casos em que você deseja interoperar com, ou porta, código que funciona na camada ABI (Interface Binária de Aplicativo).
Convertendo de e para tipos de ABI no código
Para segurança e simplicidade, para conversões em ambas as direções, você pode simplesmente usar winrt::com_ptr, com_ptr::as e winrt::Windows::Foundation::IUnknown::as. Aqui está um exemplo de código (baseado no modelo de projeto do Aplicativo de Console ), que também ilustra como você pode usar aliases de namespace para as diferentes ilhas para lidar com possíveis colisões de namespace entre a projeção C++/WinRT e a ABI.
// pch.h
#pragma once
#include <windows.foundation.h>
#include <unknwn.h>
#include "winrt/Windows.Foundation.h"
// main.cpp
#include "pch.h"
namespace winrt
{
using namespace Windows::Foundation;
}
namespace abi
{
using namespace ABI::Windows::Foundation;
};
int main()
{
winrt::init_apartment();
winrt::Uri uri(L"http://aka.ms/cppwinrt");
// Convert to an ABI type.
winrt::com_ptr<abi::IStringable> ptr{ uri.as<abi::IStringable>() };
// Convert from an ABI type.
uri = ptr.as<winrt::Uri>();
winrt::IStringable uriAsIStringable{ ptr.as<winrt::IStringable>() };
}
As implementações das funções as chamam QueryInterface. Se você quiser conversões de nível inferior que chamam apenas AddRef, então poderá usar as funções auxiliares winrt::copy_to_abi e winrt::copy_from_abi. Este próximo exemplo de código adiciona essas conversões de nível inferior ao exemplo de código acima.
Important
Ao interoperar com tipos de ABI, é fundamental que o tipo de ABI usado corresponda à interface padrão do objeto C++/WinRT. Caso contrário, invocações de métodos no tipo ABI acabarão, na verdade, chamando métodos no mesmo slot vtable na interface padrão com resultados muito inesperados. Observe que winrt::copy_to_abi não protege contra isso em tempo de compilação, pois usa void* para todos os tipos de ABI e presume que o chamador teve o cuidado de não incompatibilizar os tipos. Isso é para evitar a necessidade de cabeçalhos C++/WinRT para fazer referência a cabeçalhos ABI quando os tipos de ABI nunca podem ser usados.
int main()
{
// The code in main() already shown above remains here.
// Lower-level conversions that only call AddRef.
// Convert to an ABI type.
ptr = nullptr;
winrt::copy_to_abi(uriAsIStringable, *ptr.put_void());
// Convert from an ABI type.
uri = nullptr;
winrt::copy_from_abi(uriAsIStringable, ptr.get());
ptr = nullptr;
}
Aqui estão outras técnicas de conversões de baixo nível semelhantes, mas usando ponteiros brutos para tipos de interface ABI (aqueles definidos pelos cabeçalhos do SDK Windows) desta vez.
// The code in main() already shown above remains here.
// Copy to an owning raw ABI pointer with copy_to_abi.
abi::IStringable* owning{ nullptr };
winrt::copy_to_abi(uriAsIStringable, *reinterpret_cast<void**>(&owning));
// Copy from a raw ABI pointer.
uri = nullptr;
winrt::copy_from_abi(uriAsIStringable, owning);
owning->Release();
Para as conversões de nível mais baixo, que apenas copiam endereços, você pode usar as funções auxiliares winrt::get_abi, winrt::detach_abi e winrt::attach_abi.
WINRT_ASSERT é uma definição de macro e se expande para _ASSERTE.
// The code in main() already shown above remains here.
// Lowest-level conversions that only copy addresses
// Convert to a non-owning ABI object with get_abi.
abi::IStringable* non_owning{ reinterpret_cast<abi::IStringable*>(winrt::get_abi(uriAsIStringable)) };
WINRT_ASSERT(non_owning);
// Avoid interlocks this way.
owning = reinterpret_cast<abi::IStringable*>(winrt::detach_abi(uriAsIStringable));
WINRT_ASSERT(!uriAsIStringable);
winrt::attach_abi(uriAsIStringable, owning);
WINRT_ASSERT(uriAsIStringable);
função convert_from_abi
Essa função auxiliar converte um ponteiro de interface ABI bruto em um objeto C++/WinRT equivalente, com sobrecarga mínima.
template <typename T>
T convert_from_abi(::IUnknown* from)
{
T to{ nullptr }; // `T` is a projected type.
winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(),
winrt::put_abi(to)));
return to;
}
A função simplesmente chama QueryInterface para consultar a interface padrão do tipo C++/WinRT solicitado.
Como vimos, uma função auxiliar não é necessária para converter de um objeto C++/WinRT para o ponteiro de interface ABI equivalente. Basta usar a função de membro winrt::Windows::Foundation::IUnknown::as (ou try_as) para consultar a interface solicitada. As funções as e try_as retornam um objeto winrt::com_ptr encapsulando o tipo de ABI solicitado.
Exemplo de código usando convert_from_abi
Aqui está um exemplo de código mostrando essa função auxiliar na prática.
// pch.h
#pragma once
#include <windows.foundation.h>
#include <unknwn.h>
#include "winrt/Windows.Foundation.h"
// main.cpp
#include "pch.h"
#include <iostream>
using namespace winrt;
using namespace Windows::Foundation;
namespace winrt
{
using namespace Windows::Foundation;
}
namespace abi
{
using namespace ABI::Windows::Foundation;
};
namespace sample
{
template <typename T>
T convert_from_abi(::IUnknown* from)
{
T to{ nullptr }; // `T` is a projected type.
winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(),
winrt::put_abi(to)));
return to;
}
inline auto put_abi(winrt::hstring& object) noexcept
{
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
}
}
int main()
{
winrt::init_apartment();
winrt::Uri uri(L"http://aka.ms/cppwinrt");
std::wcout << "C++/WinRT: " << uri.Domain().c_str() << std::endl;
// Convert to an ABI type.
winrt::com_ptr<abi::IUriRuntimeClass> ptr = uri.as<abi::IUriRuntimeClass>();
winrt::hstring domain;
winrt::check_hresult(ptr->get_Domain(sample::put_abi(domain)));
std::wcout << "ABI: " << domain.c_str() << std::endl;
// Convert from an ABI type.
winrt::Uri uri_from_abi = sample::convert_from_abi<winrt::Uri>(ptr.get());
WINRT_ASSERT(uri.Domain() == uri_from_abi.Domain());
WINRT_ASSERT(uri == uri_from_abi);
}
Interoperabilidade com ponteiros para interfaces COM ABI
O modelo de função auxiliar abaixo ilustra como copiar um ponteiro de interface COM da ABI de um determinado tipo para seu tipo de ponteiro inteligente projetado C++/WinRT equivalente.
template<typename To, typename From>
To to_winrt(From* ptr)
{
To result{ nullptr };
winrt::check_hresult(ptr->QueryInterface(winrt::guid_of<To>(), winrt::put_abi(result)));
return result;
}
...
ID2D1Factory1* com_ptr{ ... };
auto cppwinrt_ptr {to_winrt<winrt::com_ptr<ID2D1Factory1>>(com_ptr)};
Este próximo modelo de função auxiliar é equivalente, exceto que ele copia do tipo de ponteiro inteligente das bibliotecas de implementação de Windows (WIL).
template<typename To, typename From, typename ErrorPolicy>
To to_winrt(wil::com_ptr_t<From, ErrorPolicy> const& ptr)
{
To result{ nullptr };
if constexpr (std::is_same_v<typename ErrorPolicy::result, void>)
{
ptr.query_to(winrt::guid_of<To>(), winrt::put_abi(result));
}
else
{
winrt::check_result(ptr.query_to(winrt::guid_of<To>(), winrt::put_abi(result)));
}
return result;
}
Consulte também Usar componentes COM com C++/WinRT.
Interoperabilidade não segura com ponteiros de interface ABI COM
A tabela a seguir mostra (além de outras operações) conversões inseguras entre um ponteiro de interface COM da ABI de um determinado tipo e seu tipo de ponteiro inteligente projetado C++/WinRT equivalente. Para o código na tabela, suponha essas declarações.
winrt::Sample s;
ISample* p;
void GetSample(_Out_ ISample** pp);
Suponha ainda que iSample seja a interface padrão para Amostra.
Você pode garantir isso em tempo de compilação com este código.
static_assert(std::is_same_v<winrt::default_interface<winrt::Sample>, winrt::ISample>);
| Operation | Como fazê-lo | Notes |
|---|---|---|
| Extrair ISample* de winrt::Sample | p = reinterpret_cast<ISample*>(get_abi(s)); |
s ainda possui o objeto. |
| Desanexar ISample* de winrt::Sample | p = reinterpret_cast<ISample*>(detach_abi(s)); |
s não é mais proprietário do objeto. |
| Transferir ISample* para o novo winrt::Sample | winrt::Sample s{ p, winrt::take_ownership_from_abi }; |
s assume a propriedade do objeto. |
| Definir ISample* em winrt::Sample | *put_abi(s) = p; |
s assume a propriedade do objeto. Qualquer objeto que antes pertencia a s causa vazamento (acionará uma asserção em modo de depuração). |
| Receber ISample* em winrt::Sample | GetSample(reinterpret_cast<ISample**>(put_abi(s))); |
s assume a propriedade do objeto. Qualquer objeto que antes pertencia a s resulta em vazamento (gerará uma asserção em modo de depuração). |
| Substituir ISample* em winrt::Sample | attach_abi(s, p); |
s assume a propriedade do objeto. O objeto anteriormente pertencente a s é liberado. |
| Copiar ISample* para winrt::Sample | copy_from_abi(s, p); |
s faz uma nova referência ao objeto. O objeto anteriormente pertencente a s é liberado. |
| Copiar winrt::Sample para ISample* | copy_to_abi(s, reinterpret_cast<void*&>(p)); |
p recebe uma cópia do objeto. Qualquer objeto que antes pertencia a p causa vazamento. |
Interoperando com a estrutura GUID da ABI
GUID (/previous-versions/aa373931(v%3Dvs.80)) é projetado como winrt::guid. Para as APIs que você implementa, você deve usar winrt::guid para parâmetros do tipo GUID. Caso contrário, há conversões automáticas entre winrt::guid e GUID , desde que você inclua unknwn.h (implicitamente incluído pelo <windows.h> e muitos outros arquivos de cabeçalho) antes de incluir quaisquer cabeçalhos C++/WinRT.
Se você não fizer isso, poderá fazer um hard-reinterpret_cast entre eles. Para a tabela a seguir, suponha essas declarações.
winrt::guid winrtguid;
GUID abiguid;
| Conversion | Com #include <unknwn.h> |
Sem #include <unknwn.h> |
|---|---|---|
| De winrt::guid para GUID | abiguid = winrtguid; |
abiguid = reinterpret_cast<GUID&>(winrtguid); |
| De GUID a winrt::guid | winrtguid = abiguid; |
winrtguid = reinterpret_cast<winrt::guid&>(abiguid); |
Você pode construir um winrt::guid como este.
winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };
Para obter uma gist mostrando como construir um winrt::guid de uma cadeia de caracteres, consulte make_guid.cpp.
Interoperando com o HSTRING da ABI
A tabela a seguir mostra conversões entre winrt::hstring e HSTRING e outras operações. Para o código na tabela, suponha essas declarações.
winrt::hstring s;
HSTRING h;
void GetString(_Out_ HSTRING* value);
| Operation | Como fazê-lo | Notes |
|---|---|---|
| Extrair HSTRING a partir de hstring | h = reinterpret_cast<HSTRING>(get_abi(s)); |
s ainda possui a cadeia de caracteres. |
| Desanexar HSTRING do hstring | h = reinterpret_cast<HSTRING>(detach_abi(s)); |
s não é mais proprietário da cadeia de caracteres. |
| Definir HSTRING em hstring | *put_abi(s) = h; |
s assume a propriedade da cadeia de caracteres. Qualquer string que antes pertencia a s causa vazamento (gerará uma asserção em modo de depuração). |
| Receber HSTRING em hstring | GetString(reinterpret_cast<HSTRING*>(put_abi(s))); |
s assume a propriedade da cadeia de caracteres. Qualquer cadeia de caracteres anteriormente pertencente a s é vazada (será declarada na depuração). |
| Substituir HSTRING no hstring | attach_abi(s, h); |
s assume a propriedade da cadeia de caracteres. A cadeia de caracteres anteriormente pertencente a s é liberada. |
| Copiar HSTRING para hstring | copy_from_abi(s, h); |
s faz uma cópia privada da cadeia de caracteres. A cadeia de caracteres anteriormente pertencente a s é liberada. |
| Copiar hstring para HSTRING | copy_to_abi(s, reinterpret_cast<void*&>(h)); |
h recebe uma cópia da cadeia de caracteres. Qualquer cadeia de caracteres anteriormente pertencente a h é vazada. |
Além disso, os auxiliares de cadeia de caracteres WIL (Bibliotecas de Implementação de Windows) executam manipulações básicas de cadeia de caracteres. Para usar os auxiliares de cadeia de caracteres WIL, inclua <wil/resource.h> e consulte a tabela abaixo. Siga os links na tabela para obter detalhes completos.
| Operation | Utilitário de string WIL para mais informações |
|---|---|
| Forneça um ponteiro bruto para uma string Unicode ou ANSI e um comprimento opcional; obtenha um wrapper unique_any adequadamente especializado | wil::make_something_string |
| Desencapsular um objeto inteligente até encontrar um ponteiro bruto para uma cadeia de caracteres Unicode terminada em nulo | wil::str_raw_ptr |
Obter a cadeia de caracteres encapsulada por um objeto de ponteiro inteligente; ou a cadeia de L"" caracteres vazia se o ponteiro inteligente estiver vazio |
wil::string_get_not_null |
| Concatenar qualquer número de cadeias de caracteres | wil::str_concat |
| Obter uma cadeia de caracteres de uma cadeia de caracteres de formato de estilo printf e uma lista de parâmetros correspondente | wil::str_printf |
APIs importantes
- Função AddRef
- Função QueryInterface
- Função winrt::attach_abi
- modelo de struct winrt::com_ptr
- Função winrt::copy_from_abi
- Função winrt::copy_to_abi
- função winrt::detach_abi
- Função winrt::get_abi
- função winrt::Windows::Foundation::IUnknown::as member
- função de membro winrt::Windows::Foundation::IUnknown::try_as
Windows developer