WRL is a C++ template library shipped with the Windows SDK 8 and 8.1. It allows to build WinRT components.
WinRT components are used in Windows Store Apps.
To deal with collections, you have to use the new Windows types. Every thing is described into the windows.foundation.collections.h header.
If you want to use Standard ISO C++ you have to investigate into this WinRT include files and its inner C++ templates.
There are no samples to provide those kind of methods that return a collection of elements except for C++/CX compiler extensions.
Let’s begin with a basic WRL project. We will provide a Root component wich can return a collection of string using the Windows HSTRING type, a collection of integers and a collection of a custom type MyData.
Here is the IDL file:
// Library1.IDL import "inspectable.idl"; import "Windows.Foundation.idl"; #define COMPONENT_VERSION 1.0 namespace Library1 { interface ILogger; runtimeclass Logger; [uuid(3EC4B4D6-14A6-4D0D-BB96-31DA25224A15), version(COMPONENT_VERSION)] interface ILogger : IInspectable { [propget] HRESULT Name([out, retval] HSTRING* value); [propput] HRESULT Name([in] HSTRING value); HRESULT LogInfo([in] HSTRING value); HRESULT GetInt32([out] int * pValue); } [version(COMPONENT_VERSION), activatable(COMPONENT_VERSION)] runtimeclass Logger { [default] interface ILogger; } declare { interface Windows.Foundation.IAsyncOperation<ILogger*>; } interface IMyData; runtimeclass MyData; [uuid(3EC5B5D7-14A6-4D0D-BB96-31DA25224A16), version(COMPONENT_VERSION)] interface IMyData : IInspectable { [propget] HRESULT Name([out, retval] HSTRING* value); [propput] HRESULT Name([in] HSTRING value); [propget] HRESULT Age([out, retval] int* value); [propput] HRESULT Age([in] int value); } [version(COMPONENT_VERSION), activatable(COMPONENT_VERSION)] runtimeclass MyData { [default] interface IMyData; } interface IRoot; runtimeclass Root; [uuid(3EC4B4D6-14A6-4D0D-BB96-31DA25224A16), version(COMPONENT_VERSION), exclusiveto(Root)] interface IRoot : IInspectable { HRESULT GetLoggerAsync([out][retval] Windows.Foundation.IAsyncOperation<ILogger*>** value); HRESULT GetVector([out][retval] Windows.Foundation.Collections.IVector<HSTRING>** value); HRESULT GetVectorInt([out][retval] Windows.Foundation.Collections.IVector<int>** value); HRESULT GetVectorData([out][retval] Windows.Foundation.Collections.IVector<IMyData *>** value); } [version(COMPONENT_VERSION), activatable(COMPONENT_VERSION)] [marshaling_behavior(agile)] [threading(both)] runtimeclass Root { [default] interface IRoot; } }
The methods named GetVector, GetVectorInt and GetVectorData will return an IVector<HSTRING>, IVector<int> and IVector<IMyData *> type.
Here is the header file for the Root component:
#pragma once #include "pch.h" #include "Library1_h.h" namespace ABI { namespace Library1 { class Root : public RuntimeClass<IRoot> { InspectableClass(L"Library1.Root", BaseTrust) public: Root() {} public: STDMETHOD(GetLoggerAsync)(IAsyncOperation<ILogger*>** value); STDMETHOD(GetVector)(IVector<HSTRING>** value); STDMETHOD(GetVectorInt)(IVector<int>** value); STDMETHOD(GetVectorData)(IVector<IMyData *>** value); }; ActivatableClass(Root); } }
Now we need to provide an implementation plumbing for IVector<T>. We need to open the windows.foundation.collections.h header and look at the various interface required to provide a valid vector collection. We will be based on a std::vector<T> implementation. Does it exists a way to transfer our standard vector to an existing C++ template ? .
If have searched for a sample, Microsoft does not provide any one…
You will find some articles but it covers the C++/CX episode with a file header named collection.h. This header is described as a “Windows Runtime Collection/Iterator Wrappers”. OK, but it is full of ref template classes with ^ syntax…
If you want to provide a real standard ISO C++, you have to open the windows.foundation.collections.h header and to find the IVector<T> interface. You will notice it is a simple COM interface with some methods to implement. We are back in the raw COM API. Here is the extract:
template <class T> struct IVector : IVector_impl<T> , detail::not_yet_specialized<IVector<T>> { }; template <class T, bool isStruct> struct IVector_impl : IInspectable /* requires IIterable<T> */ { private: typedef typename Windows::Foundation::Internal::GetAbiType<T>::type T_abi; typedef typename Windows::Foundation::Internal::GetLogicalType<T>::type T_logical; public: typedef T T_complex; // read methods virtual HRESULT STDMETHODCALLTYPE GetAt(_In_opt_ unsigned index, _Out_ T_abi *item) = 0; virtual /* propget */ HRESULT STDMETHODCALLTYPE get_Size(_Out_ unsigned *size) = 0; virtual HRESULT STDMETHODCALLTYPE GetView(_Outptr_result_maybenull_ IVectorView<T_logical> **view) = 0; virtual HRESULT STDMETHODCALLTYPE IndexOf(_In_opt_ T_abi value, _Out_ unsigned *index, _Out_ boolean *found) = 0; // write methods virtual HRESULT STDMETHODCALLTYPE SetAt(_In_ unsigned index, _In_opt_ T_abi item) = 0; virtual HRESULT STDMETHODCALLTYPE InsertAt(_In_ unsigned index, _In_opt_ T_abi item) = 0; virtual HRESULT STDMETHODCALLTYPE RemoveAt(_In_ unsigned index) = 0; virtual HRESULT STDMETHODCALLTYPE Append(_In_opt_ T_abi item) = 0; virtual HRESULT STDMETHODCALLTYPE RemoveAtEnd() = 0; virtual HRESULT STDMETHODCALLTYPE Clear() = 0; // bulk transfer methods virtual HRESULT STDMETHODCALLTYPE GetMany(_In_ unsigned startIndex, _In_ unsigned capacity, _Out_writes_to_(capacity,*actual) T_abi *value, _Out_ unsigned *actual) { return Detail::_VectorGetMany(this, startIndex, capacity, value, actual); } virtual HRESULT STDMETHODCALLTYPE ReplaceAll(_In_ unsigned count, _In_reads_(count) T_abi *value) { return Detail::_VectorReplaceAll(this, count, value); } };
So we will implement all of those methods.
My code is made with a vector<T> so let’s use T as standard template argument to our future template base class we could use this way:
STDMETHODIMP Root::GetVector(IVector<HSTRING>** value) { try { ComPtr<Vector<HSTRING>> v = Make<Vector<HSTRING>>(); HString str1; str1.Set(L"Edith"); v->Append(str1.Get()); HString str2; str2.Set(L"Lisa"); v->Append(str2.Get()); HString str3; str3.Set(L"Audrey"); v->Append(str3.Get()); *value = v.Detach(); return S_OK; } catch (...) { return E_FAIL; } } STDMETHODIMP Root::GetVectorInt(IVector<int>** value) { ComPtr<Vector<int>> v = Make<Vector<int>>(); v->Append(11); v->Append(8); v->Append(3); *value = v.Detach(); return S_OK; } STDMETHODIMP Root::GetVectorData(IVector<IMyData *>** value) { ComPtr<Vector<IMyData *>> v = Make<Vector<IMyData *>>(); HString str1; str1.Set(L"Lisa"); ComPtr<MyData> data1 = Make<MyData>(); data1->put_Name(str1.Detach()); data1->put_Age(8); v->Append(data1.Detach()); HString str2; str2.Set(L"Audrey"); ComPtr<MyData> data2 = Make<MyData>(); data2->put_Name(str2.Detach()); data2->put_Age(3); v->Append(data2.Detach()); *value = v.Detach(); return S_OK; }
You can see that we just use a simple Vector<T> template class. It takes a type and provides every thing it needs like the Append method().
We can use it with HSTRING or int or your custom type. Our internal usage is a std::vector.
All of this is standard C++ using the STL. Just look again at the Windows header file and you will find the various operations.
It maps the IList interface that exists in the .NET Framework world. It much complex that was existing on COM components for VB For each methods that only needed Count and Item methods.
We need to provide at least 3 interfaces implementations: Windows::Foundation::Collections::IVector<T>, Windows::Foundation::Collections::IIterable<T> and Windows::Foundation::Collections::IIterator<T>.
You will notice that the collection is using what we know in the STL world, the iterator.
Here is my first code a template based implementation of a vector based collection C++ template classes:
template <class T> class Iterator : public RuntimeClass<IIterator<T>> { InspectableClass(L"Library1.Iterator", BaseTrust) private: typedef typename std::vector<typename Wrap<T>::type> WrappedVector; typedef typename std::shared_ptr<WrappedVector> V; typedef typename WrappedVector::iterator IT; public: Iterator() {} void LogVectorContent() { for (IT it = _v->begin(); it != _v->end(); it++) { T element = Wrap<T>::Unwrap(*it); void * ptr = (void *)element; wchar_t sz[255]; swprintf(sz, 255, L"adress T =0x%08x\n", (int)ptr); _LogInfo(sz); } } HRESULT Init(const V& vec) { try { _LogInfo(L"Iterator::Init()..."); _v = vec; LogVectorContent(); _it = _v->begin(); if (_it != _v->end()) { _element = Wrap<T>::Unwrap(*_it); _bElement = TRUE; } else _bElement = FALSE; return S_OK; _EXCEPTION_HANDLER(L"Iterator::Init()..."); } public: virtual HRESULT STDMETHODCALLTYPE get_Current(T *current) { try { _LogInfo(L"Iterator::get_Current()..."); if (_it != _v->end()) { _bElement = TRUE; _element = Wrap<T>::Unwrap(*_it); *current = _element; } else { _bElement = FALSE; } return S_OK; _EXCEPTION_HANDLER(L"Iterator::get_Current()..."); } virtual HRESULT STDMETHODCALLTYPE get_HasCurrent(boolean *hasCurrent) { try { _LogInfo(L"Iterator::get_HasCurrent()..."); if (_bElement) *hasCurrent = TRUE; else *hasCurrent = FALSE; return S_OK; _EXCEPTION_HANDLER(L"Iterator::get_HasCurrent()..."); } virtual HRESULT STDMETHODCALLTYPE MoveNext(boolean *hasCurrent) { try { _LogInfo(L"Iterator::MoveNext()..."); if (_it != _v->end()) { ++_it; if (_it == _v->end()) { *hasCurrent = FALSE; _bElement = FALSE; } else { *hasCurrent = TRUE; _bElement = TRUE; } } else { *hasCurrent = FALSE; _bElement = FALSE; } return S_OK; _EXCEPTION_HANDLER(L"Iterator::MoveNext()..."); } private: V _v; IT _it; T _element; boolean _bElement = FALSE; }; template <typename T> class Vector : public RuntimeClass<IVector<T>, IIterable<T >> { InspectableClass(L"Library1.Vector", BaseTrust) private: typedef typename std::vector<typename Wrap<T>::type> WrappedVector; typedef typename WrappedVector::const_iterator CIT; public: Vector() { _LogInfo(L"Vector::Vector()..."); _v = std::make_shared<WrappedVector>(); } public: // read methods virtual HRESULT STDMETHODCALLTYPE GetAt(unsigned index, T *item) { _LogInfo(L"Vector::GetAt()..."); *item = Wrap<T>::Unwrap((*_v)[index]); return S_OK; } virtual HRESULT STDMETHODCALLTYPE get_Size(unsigned *size) { _LogInfo(L"Vector::get_Size()..."); *size = _v->size(); return S_OK; } virtual HRESULT STDMETHODCALLTYPE GetView(IVectorView<T> **view) { _LogInfo(L"Vector::GetView()..."); return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE IndexOf(T value, unsigned *index, boolean *found) { _LogInfo(L"Vector::IndexOf()..."); unsigned foundedIndex = 0; bool founded = false; for (CIT it = _v->cbegin() ; it != _v->end() ; ++it) { const T & v = Wrap<T>::Unwrap(*it); if (v == value) { founded = true; break; } foundedIndex++; } if (founded == true) { *found = TRUE; *index = foundedIndex; } else { *found = FALSE; } return S_OK; } // write methods virtual HRESULT STDMETHODCALLTYPE SetAt(unsigned index, T item) { _LogInfo(L"Vector::SetAt()..."); if (index < _v->size()) { (*_v)[index] = Wrap<T>::MakeWrap(item); } return S_OK; } virtual HRESULT STDMETHODCALLTYPE InsertAt(unsigned index, T item) { _LogInfo(L"Vector::InsertAt()..."); _v->insert(_v->begin() + index, Wrap<T>::MakeWrap(item)); return S_OK; } virtual HRESULT STDMETHODCALLTYPE RemoveAt(unsigned index) { _LogInfo(L"Vector::RemoveAt()..."); if (index < _v->size()) { _v->erase(_v->begin() + index); } return S_OK; } virtual HRESULT STDMETHODCALLTYPE Append(T item) { _LogInfo(L"Vector::Append()..."); _v->push_back(Wrap<T>::MakeWrap(item)); return S_OK; } virtual HRESULT STDMETHODCALLTYPE RemoveAtEnd() { _LogInfo(L"Vector::RemoveAtEnd()..."); if (!_v->empty()) { _v->pop_back(); } return S_OK; } virtual HRESULT STDMETHODCALLTYPE Clear() { _LogInfo(L"Vector::Clear()..."); _v->clear(); return S_OK; } virtual HRESULT STDMETHODCALLTYPE First(IIterator<T> **first) { _LogInfo(L"Vector::First()..."); ComPtr<Iterator<T>> p = Make<Iterator<T>>(); p->Init(_v); *first = p.Detach(); return S_OK; } private: std::shared_ptr<WrappedVector> _v; };
The magic is provided by the First() method that returns a Windows::Foundation::Collections::IIterator<T>.
You can notice that every element is wrapped and unwrapped and could ask why ? In fact, we need to take care of some special types like HSTRING or interface pointers.
HSTRING is a new Windows type that contains a reference counter so we need to increment it each time we use it.
For interface pointers, we have to make a copy with the ComPtr class and return a copy.
Here is Wrap template that handles the various cases:
template <typename T> struct Wrap { typename typedef T type; static const T& MakeWrap(const T& t) { return t; } static const T& Unwrap(const T& t) { return t; } }; template <> struct Wrap<HSTRING> { typedef HStringHelper type; static HStringHelper MakeWrap(const HSTRING& t) { HStringHelper str; str.Set(t); return str; } static HSTRING Unwrap(const HStringHelper& t) { HSTRING str; HRESULT hr = t.CopyTo(&str); // Error check return str; } }; template <typename T> struct Wrap<T*> { typename typedef ComPtr<T> type; static ComPtr<T> MakeWrap(T* t) { ComPtr<T> ptr; ptr.Attach(t); return ptr; } static T* Unwrap(ComPtr<T> t) { ComPtr<T> ptr(t); return t.Detach(); } };
You can see that HSTRING is wrapped with a class names HStringHelper.
The entire source code of this article is available on the Windows Dev Center: WRL Sample for Vector and Async