Boxing and unboxing values to IInspectable with C++/WinRT
Note
You can box and unbox not only scalar values, but also most kinds of arrays (with the exception of arrays of enumerations) by using the winrt::box_value and winrt::unbox_value functions. You can unbox only scalar values by using the winrt::unbox_value_or function.
The IInspectable interface is the root interface of every runtime class in the Windows Runtime (WinRT). This is an analogous idea to IUnknown being at the root of every COM interface and class; and System.Object being at the root of every Common Type System class.
In other words, a function that expects IInspectable can be passed an instance of any runtime class. But you can't directly pass to such a function a scalar value (such as a numeric or text value), nor an array. Instead, a scalar or array value needs to be wrapped inside a reference class object. That wrapping process is known as boxing the value.
Important
You can box and unbox any type that you can pass to a Windows Runtime API. In other words, a Windows Runtime type. Numeric and text values (strings), and arrays, are some examples given above. Another example is a struct
that you define in IDL. If you try to box a regular C++ struct
(one that's not defined in IDL), then the compiler will remind you that you can box only a Windows Runtime type. A runtime class is a Windows Runtime type, but you can of course pass runtime classes to Windows Runtime APIs without boxing them.
C++/WinRT provides the winrt::box_value function, which takes a scalar or array value, and returns the value boxed into an IInspectable. For unboxing an IInspectable back into a scalar or array value, there is the winrt::unbox_value function. For unboxing an IInspectable back into a scalar value, there is also the winrt::unbox_value_or function.
Examples of boxing a value
The LaunchActivatedEventArgs::Arguments accessor function returns a winrt::hstring, which is a scalar value. We can box that hstring value and pass it to a function that expects IInspectable like this.
void App::OnLaunched(LaunchActivatedEventArgs const& e)
{
...
rootFrame.Navigate(winrt::xaml_typename<BlankApp1::MainPage>(), winrt::box_value(e.Arguments()));
...
}
To set the content property of a XAML Button, you call the Button::Content mutator function. To set the content property to a string value, you can use this code.
Button().Content(winrt::box_value(L"Clicked"));
First, the hstring conversion constructor converts the string literal into an hstring. Then the overload of winrt::box_value that takes an hstring is invoked.
Examples of unboxing an IInspectable
In your own functions that expect IInspectable, you can use winrt::unbox_value to unbox, and you can use winrt::unbox_value_or to unbox with a default value. You can also use try_as to unbox to a std::optional.
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.
}
Determine the type of a boxed value
If you receive a boxed value and you're unsure what type it contains (you need to know its type in order to unbox it), then you can query the boxed value for its IPropertyValue interface, and then call Type on that. Here's a code example.
WINRT_ASSERT
is a macro definition, and it expands to _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);