Boxing e unboxing de valores em IInspectable com C++/WinRT

Note

Pode fazer box e unbox não só de valores escalares, mas também da maioria dos tipos de matrizes (com exceção das matrizes de enumerações), utilizando as funções winrt::box_value e winrt::unbox_value. Só podes desbloquear valores escalares usando a função winrt::unbox_value_or .

A interface IInspectable é a interface raiz de cada classe de runtime no Windows Runtime (WinRT). Esta é uma ideia análoga a IUnknown estar na raiz de todas as interfaces e classes COM; e System.Object estando na raiz de todas as classes Common Type System.

Ou seja, uma função que espera IInspectable pode receber uma instância de qualquer classe em tempo de execução. Mas não podes passar diretamente a tal função um valor escalar (como um valor numérico ou de texto), nem um array. Em vez disso, um valor escalar ou de array precisa de ser encapsulado dentro de um objeto de classe de referência. Esse processo de encapsulamento é conhecido como boxing do valor.

Important

Pode encapsular e desencapsular qualquer tipo que possa passar para qualquer API do Windows Runtime. Ou seja, um tipo de Windows Runtime. Valores numéricos e de texto (cadeias de texto), e arrays, são alguns exemplos acima. Outro exemplo é a struct que defines no IDL. Se tentares empacotar um C++ struct normal (que não esteja definido em IDL), o compilador indicar-te-á que só podes empacotar um tipo do Windows Runtime. Uma classe de runtime é um tipo Windows Runtime, mas pode, claro, passar classes de runtime para APIs do Windows Runtime sem as colocar em caixa.

C++/WinRT fornece a função winrt::box_value, que recebe um valor escalar ou uma matriz e devolve esse valor empacotado num IInspectable. Para desempacotar um IInspectable novamente num valor escalar ou matriz, existe a função winrt::unbox_value. Para desempacotar um IInspectable novamente num valor escalar, existe também a função winrt::unbox_value_or.

Exemplos de encapsulamento de um valor

A função acessora LaunchActivatedEventArgs::Arguments devolve um winrt::hstring, que é um valor escalar. Podemos encapsular esse valor hstring e passá-lo para uma função que espera IInspectable assim.

void App::OnLaunched(LaunchActivatedEventArgs const& e)
{
    ...
    rootFrame.Navigate(winrt::xaml_typename<BlankApp1::MainPage>(), winrt::box_value(e.Arguments()));
    ...
}

Para definir a propriedade de conteúdo de um Button XAML, chama a função modificadora Button::Content. Para definir a propriedade de conteúdo para um valor de string, pode usar este código.

Button().Content(winrt::box_value(L"Clicked"));

Primeiro, o construtor de conversão hstring converte o literal da string num hstring. Depois é invocada a sobrecarga de winrt::box_value que toma um hstring .

Exemplos de desencapsulamento de IInspectable

Nas tuas próprias funções que esperam IInspectable, podes usar winrt::unbox_value para desencapsular, e podes usar winrt::unbox_value_or para desencapsular com um valor predefinido. Também podes usar try_as para desembalar para um std::opcional.

void Unbox(winrt::Windows::Foundation::IInspectable const& object)
{
    hstring hstringValue = unbox_value<hstring>(object); // Throws if object is not a boxed string.
    hstringValue = unbox_value_or<hstring>(object, L"Default"); // Returns L"Default" if object is not a boxed string.
    float floatValue = unbox_value_or<float>(object, 0.f); // Returns 0.0 if object is not a boxed float.
    std::optional<int> optionalInt = object.try_as<int>(); // Returns std::nullopt if object is not a boxed int.
}

Determinar o tipo de valor em caixa

Se receberes um valor encapsulado e não tiveres a certeza de que tipo contém (precisas de saber o seu tipo para o desencapsular), podes consultar a interface IPropertyValue do valor encapsulado e depois chamar Type nessa interface. Aqui está um exemplo de código.

WINRT_ASSERT é uma definição macro, e expande-se para _ASSERTE.

float pi = 3.14f;
auto piInspectable = winrt::box_value(pi);
auto piPropertyValue = piInspectable.as<winrt::Windows::Foundation::IPropertyValue>();
WINRT_ASSERT(piPropertyValue.Type() == winrt::Windows::Foundation::PropertyType::Single);

APIs importantes