Figure 1   BankAccount

BankAccount.h


// BankAccount.h

#ifndef __BANKACCOUNT_H_
#define __BANKACCOUNT_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// OBJECTINFO

typedef struct tagOBJECTINFO {
    tagOBJECTINFO* pNext;    // Pointer to next item in list
    IUnknown* pUnknown;      // Pointer to object's IUnknown interface
    LPOLESTR pszName;        // Pointer to object name
} OBJECTINFO;

/////////////////////////////////////////////////////////////////////////////
// CNamedObjectFactory

template <class T>
class CNamedObjectFactory :
    public CComObjectRootEx<CComGlobalsThreadModel>,
    public INamedObjectFactory,
    public IExternalConnection
{
protected:
    _ATL_CREATORFUNC* m_pfnCreateInstance;

public:
    CNamedObjectFactory ()
    {
    }

BEGIN_COM_MAP(CNamedObjectFactory)
    COM_INTERFACE_ENTRY(INamedObjectFactory)
END_COM_MAP()

// INamedObjectFactory
    STDMETHODIMP CreateInstance (REFIID riid, void** ppvObject,
        LPOLESTR pszObjectName)
    {
        ATLASSERT(m_pfnCreateInstance != NULL);

        //
        // Fail the call if the name is already used.
        //
        if (T::FindObject (pszObjectName) != NULL)
            return E_FAIL;

        //
        // Create a new object instance.
        //
        HRESULT hRes = E_POINTER;
        if (ppvObject != NULL) {
            *ppvObject = NULL;
            Lock ();
            T::m_pszObjectName = pszObjectName;
            hRes = m_pfnCreateInstance(NULL, riid, ppvObject);
            Unlock ();
        }
        return hRes;
    }

    STDMETHODIMP GetInstance (REFIID riid, void** ppvObject,
        LPOLESTR pszObjectName)
    {
        //
        // Return an interface pointer on the specified object if the
        // object instance exists.
        //
        HRESULT hRes = E_POINTER;
        if (ppvObject != NULL) {
            *ppvObject = NULL;
            hRes = E_FAIL;
            OBJECTINFO* poi = T::FindObject (pszObjectName);
            if (poi != NULL)
                hRes = poi->pUnknown->QueryInterface (riid, ppvObject);
        }
        return hRes;
    }

// IExternalConnection
    STDMETHODIMP_(DWORD) AddConnection (DWORD exconn, DWORD dwReserved)
    {
        if (exconn & EXTCONN_STRONG)
            _Module.Lock ();
        return 2;
    }

    STDMETHODIMP_(DWORD) ReleaseConnection (DWORD exconn, DWORD dwReserved,
        BOOL fLastReleaseCloses)
    {
        if (exconn & EXTCONN_STRONG)
            _Module.Unlock ();
        return 1;
    }

// Helper functions
    void SetVoid(void* pv)
    {
        m_pfnCreateInstance = (_ATL_CREATORFUNC*) pv;
    }
};

#endif //__BANKACCOUNT_H_

/////////////////////////////////////////////////////////////////////////////
// CNamedObject

class ATL_NO_VTABLE CNamedObject
{
public:
    static OBJECTINFO* m_pList;                     // Pointer to object list
    static LPOLESTR m_pszObjectName;                // Pointer to object name
    static CComAutoCriticalSection m_ListCritSec;   // Gateway for list access

    static OBJECTINFO* FindObject (LPOLESTR pszObjectName)
    {
        m_ListCritSec.Lock ();
        OBJECTINFO* poi = m_pList;
        while (poi != NULL) {
            if (!_wcsicmp (poi->pszName, pszObjectName)) {
                m_ListCritSec.Unlock ();
                return poi;
            }
            poi = poi->pNext;
        }
        m_ListCritSec.Unlock ();
        return NULL;
    }

    HRESULT AddObject (IUnknown* pUnknown, LPOLESTR pszName)
    {
        //
        // Build a new OBJECTINFO structure describing the object.
        //
        ATLTRY (OBJECTINFO* pObjectInfo = new OBJECTINFO);
        if (pObjectInfo == NULL)
            return E_OUTOFMEMORY;

        int nSize = wcslen (pszName) + 1;
        ATLTRY (OLECHAR* pBuffer = new OLECHAR[nSize]);
        if (pBuffer == NULL) {
            delete pObjectInfo;
            return E_OUTOFMEMORY;
        }

        pObjectInfo->pNext = NULL;
        pObjectInfo->pUnknown = pUnknown;
        wcscpy (pBuffer, pszName);
        pObjectInfo->pszName = pBuffer;

        //
        // Add the OBJECTINFO structure to the list.
        //
        m_ListCritSec.Lock ();
        OBJECTINFO* pPrev = NULL;
        OBJECTINFO* poi = m_pList;

        while (poi != NULL) {
            pPrev = poi;
            poi = poi->pNext;
        }

        if (pPrev == NULL)
            m_pList = pObjectInfo;
        else
            pPrev->pNext = pObjectInfo;

        m_ListCritSec.Unlock ();
        return S_OK;
    }

    void RemoveObject (IUnknown* pUnknown)
    {
        m_ListCritSec.Lock ();
        OBJECTINFO* pPrev = NULL;
        OBJECTINFO* poi = m_pList;

        while (poi != NULL) {
            if (poi->pUnknown == pUnknown) {
                if (pPrev == NULL)
                    m_pList = poi->pNext;
                else
                    pPrev->pNext = poi->pNext;
                m_ListCritSec.Unlock ();
                delete[] (poi->pszName);
                delete poi;
                return;
            }
            pPrev = poi;
            poi = poi->pNext;
        }
        ATLASSERT (0); // Just to be sure
    }
};

/////////////////////////////////////////////////////////////////////////////
// CBankAccount

class ATL_NO_VTABLE CBankAccount : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CBankAccount, &CLSID_BankAccount>,
    public CNamedObject,
    public IBankAccount
{
public:
    CBankAccount()
    {
        m_lBalance = 0;
    }

    HRESULT FinalConstruct ()
    {
        return AddObject (GetUnknown (), m_pszObjectName);
    }

    void FinalRelease ()
    {
        RemoveObject (GetUnknown ());
    }

DECLARE_REGISTRY_RESOURCEID(IDR_BANKACCOUNT)
DECLARE_NOT_AGGREGATABLE(CBankAccount)
DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CBankAccount)
    COM_INTERFACE_ENTRY(IBankAccount)
END_COM_MAP()

DECLARE_CLASSFACTORY_EX (CNamedObjectFactory<CBankAccount>)

// IBankAccount
public:
    STDMETHOD(AdjustBalance)(/*[in]*/ long lAmount);
    STDMETHOD(GetBalance)(/*[out]*/ long* pBalance);
    STDMETHOD(SetBalance)(/*[in]*/ long lBalance);
protected:
    long m_lBalance;
};
BankAccount.cpp

// BankAccount.cpp : Implementation of CBankAccount
#include "stdafx.h"
#include "Bank.h"
#include "BankAccount.h"

/////////////////////////////////////////////////////////////////////////////
// CNamedObject

OBJECTINFO* CNamedObject::m_pList = NULL;
LPOLESTR CNamedObject::m_pszObjectName = NULL;
CComAutoCriticalSection CNamedObject::m_ListCritSec;

/////////////////////////////////////////////////////////////////////////////
// CBankAccount

STDMETHODIMP CBankAccount::SetBalance(long lBalance)
{
    m_lBalance = lBalance;
    return S_OK;
}

STDMETHODIMP CBankAccount::GetBalance(long *pBalance)
{
    *pBalance = m_lBalance;
    return S_OK;
}

STDMETHODIMP CBankAccount::AdjustBalance(long lAmount)
{
    m_lBalance += lAmount;
    return S_OK;
}

Figure 3   Excerpt from AcctMgrDlg.cpp


void CAcctMgrDlg::OnGetAccount() 
{
    //
    // Create the class object for bank account objects.
    //
    INamedObjectFactory* pnof;
    HRESULT hr = ::CoGetClassObject (CLSID_BankAccount, CLSCTX_SERVER,
        NULL, IID_INamedObjectFactory, (void**) &pnof);

    if (FAILED (hr)) {
        MessageBox (_T ("CoGetClassObject failed"), _T ("Error"));
        return;
    }

    //
    // Get the account name and convert it to Unicode if necessary.
    //
    CString string;
    GetDlgItemText (IDC_ACCOUNT, string);
    ASSERT (string.GetLength () != 0);

    int nSize = string.GetLength () + 1;
    OLECHAR* pName = new OLECHAR[nSize];

#ifdef UNICODE
    ::wcscpy (pName, (LPCWSTR) (LPCTSTR) string);
#else
    int n = ::MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, string, -1,
        pName, nSize);
#endif

    //
    // Try to connect to an existing bank account object.
    //
    hr = pnof->GetInstance (IID_IBankAccount, (void**) &m_pBankAccount, pName);

    //
    // If that didn't work, create a new object instance.
    //
    if (FAILED (hr)) {
        hr = pnof->CreateInstance (IID_IBankAccount,
            (void**) &m_pBankAccount, pName);

        if (FAILED (hr)) {
            MessageBox (_T ("CreateInstance failed"), _T ("Error"));
            delete[] pName;
            pnof->Release ();
            return;
        }
    }

    //
    // Get the initial balance and display it in the window.
    //
    ASSERT (m_pBankAccount != NULL);
    long lBalance;
    m_pBankAccount->GetBalance (&lBalance);
    SetDlgItemInt (IDC_BALANCE, (int) lBalance);

    //
    // Enable the dialog's button controls, clean up, and return.
    //
    GetDlgItem (IDC_REFRESH)->EnableWindow (TRUE);
    GetDlgItem (IDC_SET)->EnableWindow (TRUE);
    GetDlgItem (IDC_ADJUST)->EnableWindow (TRUE);
    delete[] pName;
    pnof->Release ();
}