401 lines
8.5 KiB
C++
401 lines
8.5 KiB
C++
#pragma once
|
|
|
|
#include <wil/resource.h>
|
|
#include <windows.foundation.h>
|
|
#include <wrl/implements.h>
|
|
#include <wrl/wrappers/corewrappers.h>
|
|
|
|
template <typename T>
|
|
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<HSTRING>
|
|
{
|
|
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 <typename T>
|
|
struct WinRTStorage<T*>
|
|
{
|
|
Microsoft::WRL::ComPtr<T> value;
|
|
|
|
HRESULT CopyTo(T** result)
|
|
{
|
|
*result = Microsoft::WRL::ComPtr<T>(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 <typename Logical, typename Abi = Logical>
|
|
struct FakeAsyncOperation : Microsoft::WRL::RuntimeClass<
|
|
ABI::Windows::Foundation::IAsyncInfo,
|
|
ABI::Windows::Foundation::IAsyncOperation<Logical>>
|
|
{
|
|
using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler<Logical>;
|
|
|
|
// 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<Handler>(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<Handler> m_handler;
|
|
ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started;
|
|
HRESULT m_result = S_OK;
|
|
WinRTStorage<Abi> m_storage;
|
|
};
|
|
|
|
template <typename Logical, typename Abi = Logical, size_t MaxSize = 250>
|
|
struct FakeVector : Microsoft::WRL::RuntimeClass<
|
|
ABI::Windows::Foundation::Collections::IVector<Logical>,
|
|
ABI::Windows::Foundation::Collections::IVectorView<Logical>>
|
|
{
|
|
// 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<unsigned>(m_size);
|
|
return S_OK;
|
|
}
|
|
|
|
IFACEMETHODIMP GetView(ABI::Windows::Foundation::Collections::IVectorView<Logical>** 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<unsigned>(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<unsigned>(count);
|
|
}
|
|
else
|
|
{
|
|
while (i--)
|
|
{
|
|
WinRTStorage<Abi>::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<Abi> m_data[MaxSize];
|
|
};
|