We saw last time that each unhappy implementation of IMemoryÂBuffer
is unhappy in its own way. How can you avoid tripping over all of these differences and defects?
Fortunately, all of the implementations satisfy the following minimum requirements:
- The underlying memory is freed when the
IMemoryÂBuffer
and allIMemoryÂBufferÂReference
s have been closed or destructed. - The objects are reliable provided you call only one method at a time.
We can operate within these minimum requirements and treat it as an external memory buffer that is wrapped inside our CustomÂMemoryÂBuffer
.
winrt::array_view<uint8_t> GetView( winrt::IMemoryBufferReference const& reference) { uint8_t* buffer; uint32_t size; winrt::check_hresult(reference.as< ABI::Windows::Foundation::IMemoryBufferByteAccess>()-> GetBuffer(&buffer, &size)); return { buffer, size }; }
We start with a generally useful function that obtains the buffer behind an IMemoryÂBufferÂReference
and returns it in the form of an array_
.
// Takes ownership of the IMemoryBufferReference winrt::IMemoryBuffer WrapAsMemoryBuffer( winrt::IMemoryBufferReference const& reference) { return CreateCustomMemoryBuffer( GetView(reference), [reference] { reference.Close(); }); }
The WrapÂAsÂMemoryÂBuffer
method takes an IMemoryÂBufferÂReference
and wraps it inside our CustomÂMemoryÂBuffer
. We call GetView
only once, and it never happens concurrently with the Close
of the buffer, so we avoid any multithreaded race conditions.
Basically, we treat IMemoryÂBufferÂReference
as just another source of memory with a cleanup function. That the memory source happens to be the same family as the wrapper we are producing is just a coincidence.
// Does not take ownership of the IMemoryBuffer winrt::IMemoryBuffer WrapMemoryBuffer( winrt::IMemoryBuffer const& buffer) { return WrapMemoryBuffer(buffer.CreateReference()); }
This overload of WrapÂMemoryÂBuffer
uses an IMemoryÂBuffer
as the source. It just creates a reference from the IMemoryÂBuffer
and then wraps that reference.
Note that the IMemoryÂBuffer
overload does not take ownership of the IMemoryÂBuffer
, since it never closes it. This is a weird asymmetry that is bound to cause confusion. Maybe it should close the IMemoryÂBuffer
?
// Takes ownership of the IMemoryBuffer winrt::IMemoryBuffer WrapMemoryBuffer( winrt::IMemoryBuffer const& buffer) { auto reference = buffer.CreateReference(); buffer.Close(); return WrapMemoryBuffer(reference); }
Alternatively, we can ask WIL to close the buffer.
// Takes ownership of the IMemoryBuffer
winrt::IMemoryBuffer WrapMemoryBuffer(
winrt::IMemoryBuffer const& buffer)
{
auto close = wil::scope_exit([&] { buffer.Close(); });
return WrapMemoryBuffer(buffer.CreateReference());
}
But maybe the function name in both cases should be something like WrapÂMemoryÂBufferÂAndÂTakeÂOwnership
? I’m not sure. You can decide.
Think of a package. If you are wrapping something, it could already be wrapped, perhaps multiple times.
If you are going to remove a previous wrapper before adding yours, then the terminology should be Rewrap