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ÂBufferand allIMemoryÂBufferÂReferences 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