如果从函数返回指向指针 COM 对象的指针,是否需要 AddRef()?
Do I need to AddRef() if returning pointer to pointer COM object from function?
下面是一个将资源映像从可执行文件加载到 ID2D1Bitmap
指针的函数。
我的问题是,我需要在 ID2D1Bitmap** ppBitmap
函数参数上调用 AddRef()
吗?
例如在函数的末尾,我需要这个吗:
(*ppBitmap)->AddRef();
我看到互联网上的代码有时会进行此类调用,有时不会,但我无法理解何时有效,何时无效?
注意:对于最小的可编译代码,我提供了完整的功能,不包括错误检查实现。
#include <sdkddkver.h>
#include <Windows.h>
#include <wincodec.h> // WIC
#include <d2d1.h> // ID2D1Bitmap
//
// Loads resource Image from executable
// into ID2D1Bitmap* pointer
//
template<typename RenderType>
HRESULT LoadResourceImage(
IWICImagingFactory* pFactory,
PCTSTR szFilename,
PCTSTR szFileType,
RenderType* pRenderTarget,
ID2D1Bitmap** ppBitmap)
{
HRESULT hr = S_OK;
DWORD dwImageSize = 0;
HMODULE hModule = GetModuleHandle(nullptr);
HRSRC hResource = nullptr;
HGLOBAL hResourceData = nullptr;
void* pImageFile = nullptr;
IWICStream* pStream = nullptr;
IWICFormatConverter* pConverter = nullptr;
IWICBitmapFrameDecode* pFrameDecode = nullptr;
IWICBitmapDecoder* pDecoder = nullptr;
if (!hModule)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
hResource = FindResource(hModule, szFilename, szFileType);
if (!hResource)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
if (FAILED(hr = hResource ? S_OK : E_FAIL))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
dwImageSize = SizeofResource(hModule, hResource);
if (!dwImageSize)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
if (FAILED(hr = dwImageSize ? S_OK : E_FAIL))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hResourceData = LoadResource(hModule, hResource);
if (!hResourceData)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
if (FAILED(hr = hResourceData ? S_OK : E_FAIL))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
pImageFile = LockResource(hResourceData);
if (!pImageFile)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
if (FAILED(hr = pImageFile ? S_OK : E_FAIL))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
if (FAILED(hr = pFactory->CreateStream(&pStream)))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hr = pStream->InitializeFromMemory(
reinterpret_cast<BYTE*>(pImageFile), dwImageSize);
if (FAILED(hr))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hr = pFactory->CreateDecoderFromStream(
pStream,
nullptr,
WICDecodeMetadataCacheOnDemand,
&pDecoder);
if (FAILED(hr))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
if (FAILED(hr = pDecoder->GetFrame(0, &pFrameDecode)))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
if (FAILED(hr = pFactory->CreateFormatConverter(&pConverter)))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hr = pConverter->Initialize(
pFrameDecode,
GUID_WICPixelFormat32bppPRGBA,
WICBitmapDitherTypeNone,
nullptr,
0.f,
WICBitmapPaletteTypeCustom);
if (FAILED(hr))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hr = pRenderTarget->CreateBitmapFromWicBitmap(
pConverter,
0,
ppBitmap);
if (FAILED(hr))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
done:
SafeRelease(&pFrameDecode);
SafeRelease(&pDecoder);
SafeRelease(&pConverter);
SafeRelease(&pStream);
return hr;
}
hr = pRenderTarget->CreateBitmapFromWicBitmap(
pConverter,
0,
ppBitmap);
成功调用上述CreateBitmapFromWicBitmap
方法返回的ID2D1Bitmap
对象,已经设置了正确的引用计数。所以,你不应该调用AddRef
。
您只需要在使用完对象后在 ID2D1Bitmap*
COM 接口指针上调用 Release
。
相反,如果您在返回的指针上再次显式调用 AddRef
,您将需要一个适当的 附加 匹配 Release
调用,否则返回的对象不会自行释放。
请注意,由于我们讨论的是 C++ 代码(而非 C 代码),您可以使用 简化 所有这些 COM 接口指针 life-cycle 管理代码]smart 像 ATL::CComPtr
这样的指针,而不是指向 COM 接口的原始指针。
CComPtr
将 自动 在包装的原始 COM 接口指针上调用 AddRef
和 Release
(例如,在范围的末尾, Release
将被 ~CComPtr
析构函数调用), 所以你不必关注这些 COM 对象 life-time 的细节。此外,同样在发生异常的情况下,Release
也会自动调用,因此在抛出异常时不会泄漏 COM 对象。
下面是一个将资源映像从可执行文件加载到 ID2D1Bitmap
指针的函数。
我的问题是,我需要在 ID2D1Bitmap** ppBitmap
函数参数上调用 AddRef()
吗?
例如在函数的末尾,我需要这个吗:
(*ppBitmap)->AddRef();
我看到互联网上的代码有时会进行此类调用,有时不会,但我无法理解何时有效,何时无效?
注意:对于最小的可编译代码,我提供了完整的功能,不包括错误检查实现。
#include <sdkddkver.h>
#include <Windows.h>
#include <wincodec.h> // WIC
#include <d2d1.h> // ID2D1Bitmap
//
// Loads resource Image from executable
// into ID2D1Bitmap* pointer
//
template<typename RenderType>
HRESULT LoadResourceImage(
IWICImagingFactory* pFactory,
PCTSTR szFilename,
PCTSTR szFileType,
RenderType* pRenderTarget,
ID2D1Bitmap** ppBitmap)
{
HRESULT hr = S_OK;
DWORD dwImageSize = 0;
HMODULE hModule = GetModuleHandle(nullptr);
HRSRC hResource = nullptr;
HGLOBAL hResourceData = nullptr;
void* pImageFile = nullptr;
IWICStream* pStream = nullptr;
IWICFormatConverter* pConverter = nullptr;
IWICBitmapFrameDecode* pFrameDecode = nullptr;
IWICBitmapDecoder* pDecoder = nullptr;
if (!hModule)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
hResource = FindResource(hModule, szFilename, szFileType);
if (!hResource)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
if (FAILED(hr = hResource ? S_OK : E_FAIL))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
dwImageSize = SizeofResource(hModule, hResource);
if (!dwImageSize)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
if (FAILED(hr = dwImageSize ? S_OK : E_FAIL))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hResourceData = LoadResource(hModule, hResource);
if (!hResourceData)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
if (FAILED(hr = hResourceData ? S_OK : E_FAIL))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
pImageFile = LockResource(hResourceData);
if (!pImageFile)
{
ShowError(__FILENAME__, __LINE__);
goto done;
}
if (FAILED(hr = pImageFile ? S_OK : E_FAIL))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
if (FAILED(hr = pFactory->CreateStream(&pStream)))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hr = pStream->InitializeFromMemory(
reinterpret_cast<BYTE*>(pImageFile), dwImageSize);
if (FAILED(hr))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hr = pFactory->CreateDecoderFromStream(
pStream,
nullptr,
WICDecodeMetadataCacheOnDemand,
&pDecoder);
if (FAILED(hr))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
if (FAILED(hr = pDecoder->GetFrame(0, &pFrameDecode)))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
if (FAILED(hr = pFactory->CreateFormatConverter(&pConverter)))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hr = pConverter->Initialize(
pFrameDecode,
GUID_WICPixelFormat32bppPRGBA,
WICBitmapDitherTypeNone,
nullptr,
0.f,
WICBitmapPaletteTypeCustom);
if (FAILED(hr))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
hr = pRenderTarget->CreateBitmapFromWicBitmap(
pConverter,
0,
ppBitmap);
if (FAILED(hr))
{
ShowError(__FILENAME__, __LINE__, hr);
goto done;
}
done:
SafeRelease(&pFrameDecode);
SafeRelease(&pDecoder);
SafeRelease(&pConverter);
SafeRelease(&pStream);
return hr;
}
hr = pRenderTarget->CreateBitmapFromWicBitmap( pConverter, 0, ppBitmap);
成功调用上述CreateBitmapFromWicBitmap
方法返回的ID2D1Bitmap
对象,已经设置了正确的引用计数。所以,你不应该调用AddRef
。
您只需要在使用完对象后在 ID2D1Bitmap*
COM 接口指针上调用 Release
。
相反,如果您在返回的指针上再次显式调用 AddRef
,您将需要一个适当的 附加 匹配 Release
调用,否则返回的对象不会自行释放。
请注意,由于我们讨论的是 C++ 代码(而非 C 代码),您可以使用 简化 所有这些 COM 接口指针 life-cycle 管理代码]smart 像 ATL::CComPtr
这样的指针,而不是指向 COM 接口的原始指针。
CComPtr
将 自动 在包装的原始 COM 接口指针上调用 AddRef
和 Release
(例如,在范围的末尾, Release
将被 ~CComPtr
析构函数调用), 所以你不必关注这些 COM 对象 life-time 的细节。此外,同样在发生异常的情况下,Release
也会自动调用,因此在抛出异常时不会泄漏 COM 对象。