如果从函数返回指向指针COM对象的指针,我是否需要AddRef()?

时间:2019-06-05 14:48:43

标签: c++ winapi com

下面是一个将资源图像从可执行文件加载到ID2D1Bitmap指针中的函数。

我的问题是,我需要在AddRef()函数参数上调用ID2D1Bitmap** ppBitmap吗?

例如在函数末尾,我是否需要这样做:

(*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;
}

1 个答案:

答案 0 :(得分:1)

hr = pRenderTarget->CreateBitmapFromWicBitmap(
        pConverter,
        0,
        ppBitmap);

成功调用上述ID2D1Bitmap方法返回的CreateBitmapFromWicBitmap对象已经已设置了正确的引用计数。因此,您不应在其上致电AddRef

完成对象后,只需在Release COM接口指针上调用ID2D1Bitmap*

相反,如果您对返回的指针再次显式调用AddRef,则需要一个与Release匹配的正确的 additional ,否则将返回返回的对象不能释放自己。

请注意,由于我们正在讨论的是C ++代码(而非C代码),因此您可以使用 smart 指针,例如{来简化所有这些COM接口指针生命周期管理代码。 {3}},而不是指向COM接口的原始指针。

CComPtr自动在包装的原始COM接口指针上调用AddRefRelease(例如,在作用域末尾{{1} }将由Release析构函数调用),因此您不必注意这些COM对象生命周期的详细信息。此外,即使在发生异常的情况下,~CComPtr也会被自动调用,因此在引发异常时您不会泄漏COM对象。