MSDN 上有很多 C# 的示例,但只有一些 C++ 的代码片段。我已经把它放在一起,我认为它会起作用,但我不确定我是否要释放所有必须的 COM 引用。
您的代码是正确的——
IBufferByteAccess
的*buffer
接口上的引用计数通过调用QueryInterface
而增加,并且您必须调用Release
一次才能释放该引用。
但是,如果你使用
ComPtr<T>
,这就变得更简单了——使用ComPtr<T>
,你不能调用IUnknown
的三个成员中的任何一个(AddRef
、Release
和QueryInterface
);它会阻止您给他们打电话。相反,它以一种很难把事情搞砸的方式封装了对这些成员函数的调用。下面是一个示例:
// Get the buffer from the WriteableBitmap:
IBuffer^ buffer = bitmap->PixelBuffer;
// Convert from C++/CX to the ABI IInspectable*:
ComPtr<IInspectable> bufferInspectable(AsInspectable(buffer));
// Get the IBufferByteAccess interface:
ComPtr<IBufferByteAccess> bufferBytes;
ThrowIfFailed(bufferInspectable.As(&bufferBytes));
// Use it:
byte* pixels(nullptr);
ThrowIfFailed(bufferBytes->Buffer(&pixels));
对
bufferInspectable.As(&bufferBytes)
的调用执行安全的 QueryInterface
:它根据 bufferBytes
的类型计算 IID,执行 QueryInterface
,并将结果指针附加到 bufferBytes
。当bufferBytes
超出范围时,会自动调用Release
。该代码与您的代码具有相同的效果,但没有容易出错的显式资源管理。
该示例使用以下两个实用程序,有助于保持代码整洁:
auto AsInspectable(Object^ const object) -> Microsoft::WRL::ComPtr<IInspectable>
{
return reinterpret_cast<IInspectable*>(object);
}
auto ThrowIfFailed(HRESULT const hr) -> void
{
if (FAILED(hr))
throw Platform::Exception::CreateException(hr);
}
细心的读者会注意到,由于此代码使用
ComPtr
来代替我们从 IInspectable*
获得的 buffer
,因此与原始代码相比,此代码实际上执行了额外的 AddRef
/Release
。我认为这种影响性能的可能性很小,最好从易于验证正确的代码开始,然后在了解热点后优化性能。
使用 C++/WinRT(而不是 C++/CX)时,有一个更方便(也更危险)的替代方案。语言投影在
data()
接口上生成 IBuffer
辅助函数,该函数将 uint8_t*
返回到内存缓冲区中。
假设
bitmap
是 WriteableBitmap 类型,代码可以缩减为:
uint8_t* pixels{ bitmap.PixelBuffer().data() };
// *** Do the work on the bytes here ***
// No cleanup required; it has already been dealt with inside data()'s implementation
在代码中,
pixels
是指向由bitmap
实例控制的数据的原始指针。因此,它仅在 bitmap
存在时才有效,但代码中没有任何内容可以帮助编译器(或读者)跟踪该依赖关系。
作为参考,WriteableBitmap::PixelBuffer
文档中有一个
示例,说明了(否则未记录的)辅助函数的使用
data()
。
扩展 Windows 运行时 API 的 C++/WinRT 函数已被记录,包括
IBuffer
接口上的函数。
这是我迄今为止尝试过的:
// Get the buffer from the WriteableBitmap
IBuffer^ buffer = bitmap->PixelBuffer;
// Get access to the base COM interface of the buffer (IUnknown)
IUnknown* pUnk = reinterpret_cast<IUnknown*>(buffer);
// Use IUnknown to get the IBufferByteAccess interface of the buffer to get access to the bytes
// This requires #include <Robuffer.h>
IBufferByteAccess* pBufferByteAccess = nullptr;
HRESULT hr = pUnk->QueryInterface(IID_PPV_ARGS(&pBufferByteAccess));
if (FAILED(hr))
{
throw Platform::Exception::CreateException(hr);
}
// Get the pointer to the bytes of the buffer
byte *pixels = nullptr;
pBufferByteAccess->Buffer(&pixels);
// *** Do the work on the bytes here ***
// Release reference to IBufferByteAccess created by QueryInterface.
// Perhaps this might be done before doing more work with the pixels buffer,
// but it's possible that without it - the buffer might get released or moved
// by the time you are done using it.
pBufferByteAccess->Release();