Skip to content
76 changes: 76 additions & 0 deletions include/wil/cppwinrt_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,51 @@ struct dispatcher_traits<winrt::Microsoft::UI::Dispatching::DispatcherQueue>
#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
/// @endcond

#if defined(WINRT_Windows_Foundation_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_BUFFER_HELPERS) ||\
defined(WIL_DOXYGEN)
Comment on lines +229 to +230
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if defined(WINRT_Windows_Foundation_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_BUFFER_HELPERS) ||\
defined(WIL_DOXYGEN)
#if defined(WINRT_Windows_Foundation_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_BUFFER_HELPERS) || defined(WIL_DOXYGEN)

To make clang-format happy

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think extra parens will also help.

/// @cond
#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_BUFFER_HELPERS
namespace Windows::Foundation
{
struct IMemoryBufferByteAccess;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
struct IMemoryBufferByteAccess;
struct IMemoryBufferByteAccess;

To make clang-format happy

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, too many instances to do this manually...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I on a fork?

One thing I'd really like to do if I ever got the time to do it is to add a pipeline/action/something that I can run that will run clang format & commit the result, either as a separate command (e.g. /azp format) or just part of the pipeline by default.

}
/// @endcond

namespace wil
{
//! Returns a view into the underlying bytes of a memory buffer
//! provided in the form of an IMemoryBufferReference.
//! The caller is responsible for ensuring that the memory buffer's
//! lifetime encompasses the lifetime of the returned view.
//! By default, returns an array_view<uint8_t>, but you can provide an alternate
//! type such as to_array_view<double>.
//! You must include memorybuffer.h in order to use this overload of to_array_view.
template<typename T = uint8_t>
winrt::array_view<T> to_array_view(winrt::Windows::Foundation::IMemoryBufferReference const& reference)
{
uint8_t* data;
uint32_t capacity;
// Make IMemoryBufferByteAccess a dependent type so we can talk about it even if <memorybuffer.h> hasn't been included.
using IMemoryBufferByteAccess = std::enable_if_t<std::is_same_v<T, T>, ::Windows::Foundation::IMemoryBufferByteAccess>;
winrt::check_hresult(reference.as<IMemoryBufferByteAccess>()->GetBuffer(&data, &capacity));
return { reinterpret_cast<T*>(data), static_cast<uint32_t>(capacity / sizeof(T)) };
}

//! Returns a view into the underlying bytes of a memory buffer
//! provided in the form of an IMemoryBuffer.
//! The caller is responsible for ensuring that the memory buffer's
//! lifetime encompasses the lifetime of the returned view.
//! By default, returns an array_view<uint8_t>, but you can provide an alternate
//! type such as to_array_view<double>.
//! You must include memorybuffer.h in order to use this overload of to_array_view.
template<typename T = uint8_t>
winrt::array_view<T> to_array_view(winrt::Windows::Foundation::IMemoryBuffer const& buffer)
{
return to_array_view<T>(buffer.CreateReference());
}
}
#endif

#if (defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS)) || \
defined(WIL_DOXYGEN)
/// @cond
Expand Down Expand Up @@ -352,6 +397,37 @@ auto to_vector(TSrc const& src)
} // namespace wil
#endif

#if defined(WINRT_Windows_Storage_Streams_H) && !defined(__WIL_CPPWINRT_WINDOWS_STORAGE_STREAMS_HELPERS) ||\
defined(WIL_DOXYGEN)
/// @cond
#define __WIL_CPPWINRT_WINDOWS_STORAGE_STREAMS_HELPERS
/// @endcond
namespace wil
{
//! Returns a view into the underlying bytes of an IBuffer up to its Length.
//! The caller is responsible for ensuring that the IBuffer's
//! lifetime encompasses the lifetime of the returned view.
//! By default, returns an array_view<uint8_t>, but you can provide an alternate
//! type such as to_array_view<double>.
template<typename T = uint8_t>
winrt::array_view<T> to_array_view(winrt::Windows::Storage::Streams::IBuffer const& buffer)
{
return { reinterpret_cast<T*>(buffer.data()), static_cast<uint32_t>(buffer.Length() / sizeof(T)) };
}

//! Returns a view into the underlying bytes of an IBuffer up to its Capacity.
//! The caller is responsible for ensuring that the IBuffer's
//! lifetime encompasses the lifetime of the returned view.
//! By default, returns an array_view<uint8_t>, but you can provide an alternate
//! type such as to_array_view<double>.
template<typename T = uint8_t>
winrt::array_view<T> to_array_view_for_capacity(winrt::Windows::Storage::Streams::IBuffer const& buffer)
{
return { reinterpret_cast<T*>(buffer.data()), static_cast<uint32_t>(buffer.Capacity() / sizeof(T)) };
}
}
#endif

#if (defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS)) || \
defined(WIL_DOXYGEN)
/// @cond
Expand Down
40 changes: 40 additions & 0 deletions tests/CppWinRTTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
#include <winrt/Windows.ApplicationModel.Activation.h>
#include <wil/cppwinrt_helpers.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.Storage.Streams.h>
#include <wil/cppwinrt_helpers.h> // Verify can include a second time to unlock more features
#include <wil/stl.h>
#include <memorybuffer.h>

using namespace winrt::Windows::ApplicationModel::Activation;

Expand Down Expand Up @@ -206,6 +208,44 @@ TEST_CASE("CppWinRTTests::VectorToVector", "[cppwinrt]")
winrt::uninit_apartment();
}

TEST_CASE("CppWinRTTests::BufferToArrayView", "[cppwinrt]")
{
std::array<int32_t, 2> testData = { 314159265, 27182818 };
auto testDataByteStart = reinterpret_cast<uint8_t*>(testData.data());

// Create a buffer with capacity for our testData, length for one of the int32_t's.
auto buffer = winrt::Windows::Storage::Streams::Buffer(sizeof(testData));
buffer.Length(sizeof(int32_t));

// Get a Capacity-based int view and set the test data.
{
auto view = wil::to_array_view_for_capacity<int32_t>(buffer);
REQUIRE(view.size() == testData.size());
std::copy(view.begin(), view.end(), testData.begin());
}
// Get a Length-based byte view and confirm that the four bytes match the first four
// bytes of our test data.
{
auto view = wil::to_array_view(buffer);
REQUIRE(view.size() == sizeof(int32_t));
REQUIRE(view == winrt::array_view(testDataByteStart, sizeof(int32_t)));
}
// Create an IMemoryBuffer around the Buffer. This uses the Buffer's Capacity as the MemoryBuffer size.
auto mbuffer = winrt::Windows::Storage::Streams::Buffer::CreateMemoryBufferOverIBuffer(buffer);
// Verify that the buffer is the test data as int32_t.
{
auto view = wil::to_array_view<int32_t>(mbuffer);
REQUIRE(view.size() == testData.size());
REQUIRE(view == winrt::array_view(testData));
}
// Verify that the buffer reference gives us the test data as uint8_t.
{
auto view = wil::to_array_view(mbuffer.CreateReference());
REQUIRE(view.size() == sizeof(testData));
REQUIRE(view == winrt::array_view(testDataByteStart, sizeof(testData)));
}
}

TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr) {
Expand Down
Loading