I'm implementing an interface that returns a DeviceInformationCollection
. The implementation can time out (or fail), in which case I would like to return an empty collection. This is to allow clients of the interface to always iterate over the returned collection, regardless of whether it succeeded or not, e.g.
auto&& devices{ co_await MyType::GetDevicesAsync() };
for (auto&& device : devices)
{
// Do crazy stuff with 'device'
}
However, I cannot figure out, how to construct an empty DeviceInformationCollection
. The following code 'works', but causes undefined behavior when clients use the code above:
IAsyncOperation<DeviceInformationCollection> MyType::GetDevicesAsync()
{
// Doing Guru Meditation
// ...
co_return { nullptr };
}
My current workaround is to return an IVector<DeviceInformation>
instead, and copy the items of the internal DeviceInformationCollection
into the vector on success. That's both tedious as well as inefficient. I'd much rather just return the DeviceInformationCollection
as-is, and construct an empty collection on failure.
Is there a way to do this?
Officially, this is not supported as the DeviceInformationCollection class does not provide a way to create an empty instance of itself. Unless you can find some function in the Windows.Devices.Enumeration API that does this for you you're out of luck.
Unofficially, we can observe that the default interface for the DeviceInformationCollection class is IVectorView. This means that this interface represents the class on the ABI. So you can play tricks with this knowledge but in general, this is very dangerous because APIs that accept a DeviceInformationCollection as input may assume that its implementation is exclusive and thus rely on some internal layout that you may not be aware of. Better to return IVectorView every time in a polymorphic and safe manner. Something like this:
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Devices::Enumeration;
IAsyncOperation<IVectorView<DeviceInformation>> Async()
{
DeviceInformationCollection devices = co_await // ... some async call
if (devices)
{
co_return devices;
}
// Returns empty IVectorView...
co_return single_threaded_observable_vector<DeviceInformation>().GetView();
}
int main()
{
for (auto&& device : Async().get())
{
printf("%ls\n", device.Name().c_str());
}
}