カタバミさんのプログラミングノート

日曜プログラマーがプログラミング関係のメモを記録するブログです。

ATLのCAtlArray<CComPtr<...>>の動作確認

ATLのCAtlArray<CComPtr<...>>クラスの動作確認用サンプルコードです。CAtlArrayクラスは自身のデストラクタで要素のデストラクタを呼び出すかどうかを確認するために作成しました。結論として、CAtlArray<CComPtr<...>>クラスは自力で個々の要素を解放することなくデストラクタを呼び出し、要素の型としてCComPtrクラスを与えた場合はメモリを適切に管理できます。

#define STRICT
#include <Windows.h>

#include <atlbase.h>
#include <atlcom.h>
#include <atlcoll.h>
#include <atlstr.h>

class CClassFactory : IClassFactory
{
private:
    ULONG m_cRef;
    int m_n;
public:
    CClassFactory(int n);
    ~CClassFactory();
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
    HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
    HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock);
private:
    void OutputDebugStringForMethod(LPCTSTR method);
};

void test1()
{
    CComPtr<IUnknown> pUnknown1((IUnknown*)new CClassFactory(1));
    // CClassFactory(1)::CClassFactory()
    // CClassFactory(1)::AddRef()
    CComPtr<IUnknown> pUnknown2((IUnknown*)new CClassFactory(2));
    // CClassFactory(2)::CClassFactory()
    // CClassFactory(2)::AddRef()
    CComPtr<IUnknown> pUnknown3((IUnknown*)new CClassFactory(3));
    // CClassFactory(3)::CClassFactory()
    // CClassFactory(3)::AddRef()

    CAtlArray<CComPtr<IUnknown>> unknowns;
    unknowns.Add(pUnknown1);
    // CClassFactory(1)::AddRef()
    unknowns.Add(pUnknown2);
    // CClassFactory(2)::AddRef()
    unknowns.Add(pUnknown3);
    // CClassFactory(3)::AddRef()

    unknowns.RemoveAll();
    // CClassFactory(1)::Release()
    // CClassFactory(2)::Release()
    // CClassFactory(3)::Release()
    // CClassFactory(3)::Release() <Final>
    // CClassFactory(3)::~CClassFactory()
    // CClassFactory(2)::Release() <Final>
    // CClassFactory(2)::~CClassFactory()
    // CClassFactory(1)::Release() <Final>
    // CClassFactory(1)::~CClassFactory()
}

void test2()
{
    CComPtr<IUnknown> pUnknown1((IUnknown*)new CClassFactory(1));
    // CClassFactory(1)::CClassFactory()
    // CClassFactory(1)::AddRef()
    CComPtr<IUnknown> pUnknown2((IUnknown*)new CClassFactory(2));
    // CClassFactory(2)::CClassFactory()
    // CClassFactory(2)::AddRef()
    CComPtr<IUnknown> pUnknown3((IUnknown*)new CClassFactory(3));
    // CClassFactory(3)::CClassFactory()
    // CClassFactory(3)::AddRef()

    CAtlArray<CComPtr<IUnknown>> unknowns;
    unknowns.Add(pUnknown1);
    // CClassFactory(1)::AddRef()
    unknowns.Add(pUnknown2);
    // CClassFactory(2)::AddRef()
    unknowns.Add(pUnknown3);
    // CClassFactory(3)::AddRef()

    // CClassFactory(1)::Release()
    // CClassFactory(2)::Release()
    // CClassFactory(3)::Release()
    // CClassFactory(3)::Release() <Final>
    // CClassFactory(3)::~CClassFactory()
    // CClassFactory(2)::Release() <Final>
    // CClassFactory(2)::~CClassFactory()
    // CClassFactory(1)::Release() <Final>
    // CClassFactory(1)::~CClassFactory()
}

int main()
{
    test1();
    test2();
    return 0;
}

CClassFactory::CClassFactory(int n)
    : m_cRef(0), m_n(n)
{
    OutputDebugStringForMethod(TEXT("CClassFactory()"));
}

CClassFactory::~CClassFactory()
{
    OutputDebugStringForMethod(TEXT("~CClassFactory()"));
}

HRESULT STDMETHODCALLTYPE CClassFactory::QueryInterface(
    REFIID riid,
    void **ppvObject)
{
    if (ppvObject == nullptr)
    {
        OutputDebugStringForMethod(TEXT("QueryInterface(...) -> E_POINTER"));
        return E_POINTER;
    }
    if (IsEqualIID(riid, IID_IUnknown))
    {
        m_cRef++;
        OutputDebugStringForMethod(TEXT("QueryInterface(IID_IUnknown)"));
        *ppvObject = reinterpret_cast<IUnknown*>(this);
        return S_OK;
    }
    else if (IsEqualIID(riid, IID_IClassFactory))
    {
        m_cRef++;
        OutputDebugStringForMethod(TEXT("QueryInterface(IID_IClassFactory)"));
        *ppvObject = reinterpret_cast<IClassFactory*>(this);
        return S_OK;
    }
    else
    {
        OutputDebugStringForMethod(TEXT("QueryInterface(<Unknown>)"));
        return E_FAIL;
    }
}

ULONG STDMETHODCALLTYPE CClassFactory::AddRef()
{
    OutputDebugStringForMethod(TEXT("AddRef()"));
    return ++m_cRef;
}

ULONG STDMETHODCALLTYPE CClassFactory::Release()
{
    if (--m_cRef == 0)
    {
        OutputDebugStringForMethod(TEXT("Release() <Final>"));
        delete this;
        return 0;
    }
    else
    {
        OutputDebugStringForMethod(TEXT("Release()"));
    }
    return m_cRef;
}

HRESULT STDMETHODCALLTYPE CClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
{
    OutputDebugStringForMethod(TEXT("CreateInstance(...)"));
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE CClassFactory::LockServer(BOOL fLock)
{
    OutputDebugStringForMethod(TEXT("LockServer(...)"));
    return E_NOTIMPL;
}

void CClassFactory::OutputDebugStringForMethod(LPCTSTR method)
{
    CStringW s;
    s.Format(L"CClassFactory(%i)::%s\r\n", m_n, method);
    OutputDebugStringW(s);
}