如果从函数返回指向指针 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 管理代码]smartATL::CComPtr 这样的指针,而不是指向 COM 接口的原始指针。

CComPtr 自动 在包装的原始 COM 接口指针上调用 AddRefRelease(例如,在范围的末尾, Release 将被 ~CComPtr 析构函数调用), 所以你不必关注这些 COM 对象 life-time 的细节。此外,同样在发生异常的情况下,Release 也会自动调用,因此在抛出异常时不会泄漏 COM 对象。