#pragma once #include #include #include #include template struct WinRTStorage { T value = {}; HRESULT CopyTo(T* result) { *result = value; return S_OK; } HRESULT Set(T val) { value = val; return S_OK; } static void Destroy(T&) { } void Reset() { } bool Equals(T val) { // NOTE: Padding can through this off, but this isn't intended to be a robust solution... return memcmp(&value, &val, sizeof(T)) == 0; } }; template <> struct WinRTStorage { Microsoft::WRL::Wrappers::HString value; HRESULT CopyTo(HSTRING* result) const { return value.CopyTo(result); } HRESULT Set(HSTRING val) { return value.Set(val); } static void Destroy(HSTRING& val) { ::WindowsDeleteString(val); val = nullptr; } void Reset() { value = {}; } bool Equals(HSTRING val) const { return value == val; } }; template struct WinRTStorage { Microsoft::WRL::ComPtr value; HRESULT CopyTo(T** result) { *result = Microsoft::WRL::ComPtr(value).Detach(); return S_OK; } HRESULT Set(T* val) { value = val; return S_OK; } static void Destroy(T*& val) { val->Release(); val = nullptr; } void Reset() { value.Reset(); } bool Equals(T* val) { return value.Get() == val; } }; // Very minimal IAsyncOperation implementation that gives calling tests control over when it completes template struct FakeAsyncOperation : Microsoft::WRL::RuntimeClass< ABI::Windows::Foundation::IAsyncInfo, ABI::Windows::Foundation::IAsyncOperation> { using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler; // IAsyncInfo IFACEMETHODIMP get_Id(unsigned int*) override { return E_NOTIMPL; } IFACEMETHODIMP get_Status(AsyncStatus* status) override { auto lock = m_lock.lock_shared(); *status = m_status; return S_OK; } IFACEMETHODIMP get_ErrorCode(HRESULT* errorCode) override { auto lock = m_lock.lock_shared(); *errorCode = m_result; return S_OK; } IFACEMETHODIMP Cancel() override { return E_NOTIMPL; } IFACEMETHODIMP Close() override { return E_NOTIMPL; } // IAsyncOperation IFACEMETHODIMP put_Completed(Handler* handler) override { bool invoke = false; { auto lock = m_lock.lock_exclusive(); if (m_handler) { return E_FAIL; } m_handler = handler; invoke = m_status != ABI::Windows::Foundation::AsyncStatus::Started; } if (invoke) { handler->Invoke(this, m_status); } return S_OK; } IFACEMETHODIMP get_Completed(Handler** handler) override { auto lock = m_lock.lock_shared(); *handler = Microsoft::WRL::ComPtr(m_handler).Detach(); return S_OK; } IFACEMETHODIMP GetResults(Abi* results) override { return m_storage.CopyTo(results); } // Test functions void Complete(HRESULT hr, Abi result) { using namespace ABI::Windows::Foundation; Handler* handler = nullptr; { auto lock = m_lock.lock_exclusive(); if (m_status == AsyncStatus::Started) { m_result = hr; m_storage.Set(result); m_status = SUCCEEDED(hr) ? AsyncStatus::Completed : AsyncStatus::Error; handler = m_handler.Get(); } } if (handler) { handler->Invoke(this, m_status); } } private: wil::srwlock m_lock; Microsoft::WRL::ComPtr m_handler; ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started; HRESULT m_result = S_OK; WinRTStorage m_storage; }; template struct FakeVector : Microsoft::WRL::RuntimeClass< ABI::Windows::Foundation::Collections::IVector, ABI::Windows::Foundation::Collections::IVectorView> { // IVector IFACEMETHODIMP GetAt(unsigned index, Abi* item) override { if (index >= m_size) { return E_BOUNDS; } return m_data[index].CopyTo(item); } IFACEMETHODIMP get_Size(unsigned* size) override { *size = static_cast(m_size); return S_OK; } IFACEMETHODIMP GetView(ABI::Windows::Foundation::Collections::IVectorView** view) override { this->AddRef(); *view = this; return S_OK; } IFACEMETHODIMP IndexOf(Abi value, unsigned* index, boolean* found) override { for (size_t i = 0; i < m_size; ++i) { if (m_data[i].Equals(value)) { *index = static_cast(i); *found = true; return S_OK; } } *index = 0; *found = false; return S_OK; } IFACEMETHODIMP SetAt(unsigned index, Abi item) override { if (index >= m_size) { return E_BOUNDS; } return m_data[index].Set(item); } IFACEMETHODIMP InsertAt(unsigned index, Abi item) override { // Insert at the end and swap it into place if (index > m_size) { return E_BOUNDS; } auto hr = Append(item); if (SUCCEEDED(hr)) { for (size_t i = m_size - 1; i > index; --i) { wistd::swap_wil(m_data[i], m_data[i - 1]); } } return hr; } IFACEMETHODIMP RemoveAt(unsigned index) override { if (index >= m_size) { return E_BOUNDS; } for (size_t i = index + 1; i < m_size; ++i) { wistd::swap_wil(m_data[i], m_data[i - 1]); } m_data[--m_size].Reset(); return S_OK; } IFACEMETHODIMP Append(Abi item) override { if (m_size > MaxSize) { return E_OUTOFMEMORY; } auto hr = m_data[m_size].Set(item); if (SUCCEEDED(hr)) { ++m_size; } return hr; } IFACEMETHODIMP RemoveAtEnd() override { if (m_size == 0) { return E_BOUNDS; } m_data[--m_size].Reset(); return S_OK; } IFACEMETHODIMP Clear() override { for (size_t i = 0; i < m_size; ++i) { m_data[i].Reset(); } m_size = 0; return S_OK; } IFACEMETHODIMP GetMany(unsigned startIndex, unsigned capacity, Abi* value, unsigned* actual) override { *actual = 0; if (startIndex >= m_size) { return S_OK; } auto count = m_size - startIndex; count = (count > capacity) ? capacity : count; HRESULT hr = S_OK; unsigned i; for (i = 0; (i < count) && SUCCEEDED(hr); ++i) { hr = m_data[startIndex + i].CopyTo(value + i); } if (SUCCEEDED(hr)) { *actual = static_cast(count); } else { while (i--) { WinRTStorage::Destroy(value[i]); } } return hr; } IFACEMETHODIMP ReplaceAll(unsigned count, Abi* value) override { if (count > MaxSize) { return E_OUTOFMEMORY; } Clear(); HRESULT hr = S_OK; for (size_t i = 0; (i < count) && SUCCEEDED(hr); ++i) { hr = m_data[i].Set(value[i]); } if (FAILED(hr)) { Clear(); } return hr; } private: size_t m_size = 0; WinRTStorage m_data[MaxSize]; };