Figure 1   Using CoGetClassObject

COLORREF GetPink(void) {
  IColorClass *pcc = 0; short r, g, b;
  HRESULT hr = CoGetClassObject(CLSID_Color, CLSCTX_ALL, 0,
                                IID_IColorClass,(void**)&pcc);
  if (SUCCEEDED(hr)) {
    IColor *pink = 0; 
    hr = pcc->CreateColor(255, 100, 100, &pink);
    if (SUCCEEDED(hr)) {
      pink->get_Red(&r); pink->get_Green(&g); 
      pink->get_Blue(&b);
      pink->Release(); 
    }
    pcc->Release();
  }
  return RGB(r,g,b);
}


Figure 2   Emulating Visual Basic GetObject

IUnknown *GetObject(LPCOLESTR wszObjectName) {
  IUnknown *pUnk = 0;
  IBindCtx *pbc = 0;
  HRESULT hr = CreateBindCtx(&pbc, 0);
  if (SUCCEEDED(hr)) {
    ULONG cch;
    IMoniker *pmk = 0;
    hr = MkParseDisplayName(pbc, wszObjectName,
                            &cch, &pmk);
    if (SUCCEEDED(hr)) {
      hr = pmk->BindToObject(pbc, 0, IID_IUnknown,
                             (void**)&pUnk);
      pmk->Release();
    }
    pbc->Release();
  }
  return pUnk;
}


Figure 3   Class Moniker BindToObject Pseudocode

HRESULT 
CStdClassMoniker::BindToObject(IBindCtx *pbc, IMoniker *pmkToLeft, 
                               REFIID riid, void**ppv){
  BIND_OPTS2 bo; bo.cbStruct = sizeof(bo);
  pbc->GetBindOptions(&bo);

  if (pmkToLeft != 0) { // we are being composed
    IClassActivator *pca = 0;
// bind the activation context to our left
    hr = pmkToLeft->BindToObject(pbc, 0, IID_IClassActivator,
                                 (void**)&pca):
    if (SUCCEEDED(hr)) {
// ask the activator for a class object
      hr = pca->GetClassObject(m_clsid, bo.dwClassContext,
                               bo.locale, riid, ppv);
      pca->Release();
    }
    return hr;
  }
  else return CoGetClassObject(m_clsid, bo.dwClassContext,
                               0, riid, ppv);
}


Figure 5   ColorClass ATL Implementation

////////////////////////////////////////////
//
// ATLColor.h - 1997, Don Box
//
// An ATL-based inplementation of IColor/IColorClass
//

#ifndef __COLOR_H_
#define __COLOR_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// Color
class Color : 
    public CComObjectRootEx<CComMultiThreadModelNoCS>,
    public IColor
{
BEGIN_COM_MAP(Color)
    COM_INTERFACE_ENTRY(IColor)
END_COM_MAP()
    short m_red; short m_green; short m_blue;
public:
// we need to explicitly lock/unlock module because CComObject<>
// will not work with classes that do not have default constructors

    Color(short r, short g, short b)
        : m_red(r), m_green(g), m_blue(b)
    {
        _Module.Lock();
    }

    ~Color(void)
    {
        _Module.Unlock();
    }

// IUnknown methods (needed due to CComObject-incompatibility)
    STDMETHODIMP QueryInterface(REFIID iid, void ** ppvObject)
    {return _InternalQueryInterface(iid, ppvObject);}
    STDMETHODIMP_(ULONG) AddRef(void) 
    {return InternalAddRef();}
    STDMETHODIMP_(ULONG) Release(void){
        ULONG l = InternalRelease();
        if (l == 0)
            delete this;
        return l;
    }

// IColor methods
    STDMETHODIMP get_Red(/*[out, retval]*/ short *pval)
    { *pval = m_red; return S_OK; }
    STDMETHODIMP get_Green(/*[out, retval]*/ short *pval)
    { *pval = m_green; return S_OK; }
    STDMETHODIMP get_Blue(/*[out, retval]*/ short *pval)
    { *pval = m_blue; return S_OK; }
    
// ColorClass will act as the class object for our class
    class ColorClass : 
        public CComObjectRootEx<CComMultiThreadModelNoCS>,
        public IColorClass,
        public IExternalConnection
    {
    BEGIN_COM_MAP(ColorClass)
        COM_INTERFACE_ENTRY(IColorClass)
        COM_INTERFACE_ENTRY(IExternalConnection)
    END_COM_MAP()
    // IColorClass methods
        STDMETHODIMP CreateColor(short r, short g, short b,
                                  IColor **ppc) {
            if ((*ppc = new Color(r, g, b)) == 0)
                return E_OUTOFMEMORY;
            (*ppc)->AddRef();
            return S_OK;
        }

    // IExternalConnection methods
        STDMETHODIMP_(DWORD) AddConnection(DWORD extconn, DWORD) {
            if (extconn&EXTCONN_STRONG) _Module.Lock();
            return 2;
        }
        STDMETHODIMP_(DWORD) ReleaseConnection(DWORD extconn, DWORD, BOOL) {
            if (extconn&EXTCONN_STRONG) _Module.Unlock();
            return 1;
        }
    };
// make ColorClass our class object C++ class
DECLARE_CLASSFACTORY_EX(ColorClass)

DECLARE_REGISTRY_RESOURCEID(IDR_COLOR)
static const CLSID& WINAPI GetObjectCLSID() {return CLSID_Color;}
static LPCTSTR WINAPI GetObjectDescription() {return NULL;}
typedef CComFailCreator<E_FAIL>  _CreatorClass;
};

#endif //__COLOR_H_


Figure 6   TimeOfDay

//
// TimeOfDay.h - 1997, Don Box
//
// A singleton implementation of ITimeOfDay
//

#ifndef __TOD_H_
#define __TOD_H_

class TimeOfDay : 
    public ITimeOfDay,
    public IExternalConnection
{
public:
    double m_offset;

// IUnknown methods 
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        if (riid == IID_IUnknown || riid == IID_ITimeOfDay)
            *ppv = static_cast<ITimeOfDay*>(this);
        else if (riid == IID_IExternalConnection)
            *ppv = static_cast<IExternalConnection*>(this);
        else
            return (*ppv = 0), E_NOINTERFACE;
        ((IUnknown*)*ppv)->AddRef();
        return S_OK;
    }
    STDMETHODIMP_(ULONG) AddRef(void) 
    { return 2;}
    STDMETHODIMP_(ULONG) Release(void)
    { return 1; }

// ITimeOfDay methods
    STDMETHODIMP GetCurrentTimeOfDay(DATE *pval)
    { 
        SYSTEMTIME st;
// convert system time to a date
        GetLocalTime(&st);
        SystemTimeToVariantTime(&st, pval);
// factor in timezone offsets
        *pval += m_offset;
        return S_OK;
    }

// IExternalConnection methods
    STDMETHODIMP_(DWORD) AddConnection(DWORD extconn, DWORD) {
        extern void LockModule();// some module locking routine
        if (extconn&EXTCONN_STRONG) 
            LockModule(); 
        return 2;
    }
    STDMETHODIMP_(DWORD) ReleaseConnection(DWORD extconn, DWORD, BOOL) {
        extern void UnlockModule();// some module unlocking routine
        if (extconn&EXTCONN_STRONG) 
            UnlockModule();
        return 1;
    }
};

#endif

Figure 8   Per-Time Zone TimeOfDay Singleton

TOD.idl

////////////////////////////////////////////
//
// TOD.idl - 1997, Don Box
//
// Interface definitions for TimeOfDay
//

[
    uuid(8C54EFA0-B85F-11d0-8C3E-0080C73925BA),
    object,
    oleautomation
]
interface ITimeOfDay : IUnknown
{
    import "oaidl.idl";
    HRESULT GetCurrentTimeOfDay([out, retval] DATE *pval);
}

[
    uuid(8C54EFA1-B85F-11d0-8C3E-0080C73925BA),
    helpstring("Time Of Day Interfaces"),
    lcid(0),
    version(1.0)
]
library TimeOfDayLib
{
    [
        uuid(8C54EFA2-B85F-11d0-8C3E-0080C73925BA)
    ]
    coclass TimeOfDay
    {
        interface ITimeOfDay;
    }
}
TZ.cpp

////////////////////////////////////////////
//
// TZ.cpp - 1997, Don Box
//
// A multi-singleton implementation of ITimeOfDay
// that supports multiple time zones using composite 
// monikers.
//
// Usage: 
//   Dim tod as ITimeOfDay
//   Set tod = GetObject("clsid:8C54EFA2-B85F-11d0-8C3E-0080C73925BA:!Eastern")
//   MsgBox tod.GetCurrentTimeOfDay()
//


#define _WIN32_WINNT 0x402
#include <windows.h>
#include "TOD.h"
#include "TOD_i.c"
#include "TimeOfDay.h"

class TODClass : 
    public IOleItemContainer,
	public IExternalConnection,
    public ITimeOfDay
{
// this object supports four time zones
    TimeOfDay m_rgTimes[4];
public:
    TODClass(void) 
    {
// initialize the offsets to adjust for timezone shifts
        m_rgTimes[0].m_offset = 0;
        m_rgTimes[1].m_offset = 1/24.0;
        m_rgTimes[2].m_offset = 2/24.0;
        m_rgTimes[3].m_offset = 3/24.0;
    }

// IUnknown methods 
	STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
	{
        if (riid == IID_IUnknown || riid == IID_ITimeOfDay)
            *ppv = static_cast<ITimeOfDay*>(this);
        else if (riid == IID_IExternalConnection)
            *ppv = static_cast<IExternalConnection*>(this);
        else if (riid == IID_IParseDisplayName)
            *ppv = static_cast<IParseDisplayName*>(this);
        else if (riid == IID_IOleContainer)
            *ppv = static_cast<IOleContainer*>(this);
        else if (riid == IID_IOleItemContainer)
            *ppv = static_cast<IOleItemContainer*>(this);
        else
            return (*ppv = 0), E_NOINTERFACE;
        ((IUnknown*)*ppv)->AddRef();
        return S_OK;
    }
    STDMETHODIMP_(ULONG) AddRef(void) 
    { return 2;}
	STDMETHODIMP_(ULONG) Release(void)
    { return 1; }

// ITimeOfDay methods
	STDMETHODIMP GetCurrentTimeOfDay(DATE *pval)
    { 
// class object also implements ITimeOfDay (maps to 
// pacific time)
        return m_rgTimes[0].GetCurrentTimeOfDay(pval);
    }

// IExternalConnection methods
    STDMETHODIMP_(DWORD) AddConnection(DWORD extconn, DWORD) {
        extern void LockModule();// some module locking routine
        if (extconn&EXTCONN_STRONG) 
            LockModule(); 
        return 2;
    }
    STDMETHODIMP_(DWORD) ReleaseConnection(DWORD extconn, DWORD, BOOL) {
        extern void UnlockModule();// some module unlocking routine
        if (extconn&EXTCONN_STRONG) 
            UnlockModule();
        return 1;
    }

// IParseDisplayName methods
    STDMETHODIMP 
    ParseDisplayName( IBindCtx *pbc,
                      LPOLESTR pszDisplayName,
                      ULONG *pchEaten,
                      IMoniker **ppmkOut)
    {
// parse string as an item moniker, stripping off the leading "!"
        *pchEaten = wcslen(pszDisplayName);
        return CreateItemMoniker(OLESTR("!"), pszDisplayName + 1, ppmkOut);
    }

    // IOleContainer methods
    STDMETHODIMP 
    EnumObjects(DWORD grfFlags, IEnumUnknown **ppenum)
    {
        *ppenum = 0;
        return E_NOTIMPL;
    }


    STDMETHODIMP 
    LockContainer(BOOL fLock)
    {
        return E_NOTIMPL;
    }


    // IOleContainer methods
    STDMETHODIMP 
    GetObject(LPOLESTR pszItem, DWORD dwSpeedNeeded,
              IBindCtx *pbc, REFIID riid, void **ppv)
    {

        if (_wcsicmp(pszItem, OLESTR("pacific")) == 0)
            return m_rgTimes[0].QueryInterface(riid, ppv);
        else if (_wcsicmp(pszItem, OLESTR("mountain")) == 0)
            return m_rgTimes[1].QueryInterface(riid, ppv);
        else if (_wcsicmp(pszItem, OLESTR("central")) == 0)
            return m_rgTimes[2].QueryInterface(riid, ppv);
        else if (_wcsicmp(pszItem, OLESTR("eastern")) == 0)
            return m_rgTimes[3].QueryInterface(riid, ppv);
        
        *ppv = 0;
        return MK_E_NOOBJECT;
    }

    STDMETHODIMP 
    GetObjectStorage(LPOLESTR pszItem, IBindCtx *pbc, REFIID riid, void **ppv)
    {
        *ppv = 0;
        return MK_E_NOSTORAGE;
    }

    STDMETHODIMP 
    IsRunning(LPOLESTR pszItem)
    {
        return E_NOTIMPL;
    }

};

HANDLE g_heventDone = CreateEvent(0, TRUE, FALSE, 0);
void LockModule()
{
    CoAddRefServerProcess();
}

void UnlockModule()
{
    if (CoReleaseServerProcess() == 0)
        SetEvent(g_heventDone);
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR szCmdParam, int)
{
    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hr))
        return hr;
    if (strstr(szCmdParam, "/RegServer") == 0)
    {
        DWORD dwReg;
        TODClass todClassObject;
        hr = CoRegisterClassObject(CLSID_TimeOfDay,
                                   (IExternalConnection*)&todClassObject,
                                   CLSCTX_LOCAL_SERVER,
                                   REGCLS_MULTIPLEUSE,
                                   &dwReg);
        if (SUCCEEDED(hr))
        {
            WaitForSingleObject(g_heventDone, INFINITE);
            CoRevokeClassObject(dwReg);
        }
    }
    else
    {
// self-registration code
        char szFileName[MAX_PATH];
        GetModuleFileNameA(0, szFileName, MAX_PATH);
        OLECHAR wszFileName[MAX_PATH];
        mbstowcs(wszFileName, szFileName, MAX_PATH);
        ITypeLib *ptl = 0;
        hr = LoadTypeLib(wszFileName, &ptl);
        if (SUCCEEDED(hr))
        {
            hr = RegisterTypeLib(ptl, wszFileName, 0);
            ptl->Release();
        }
        RegSetValueA(HKEY_CLASSES_ROOT, "CLSID\\{8C54EFA2-B85F-11d0-8C3E-
0080C73925BA}", REG_SZ, "TimeOfDay Object", 23);
        RegSetValueA(HKEY_CLASSES_ROOT, "CLSID\\{8C54EFA2-B85F-11d0-8C3E-
0080C73925BA}\\LocalServer32", REG_SZ, szFileName, lstrlenA(szFileName));
    }
    CoUninitialize();
    return hr;
}

Figure 10  Multi-Time Zone TimeOfDay Using Monikers

Form1.FRM
VERSION 5.00
Begin VB.Form Form1 
   Caption         =   "It's Moniker Time!"
   ClientHeight    =   2475
   ClientLeft      =   240
   ClientTop       =   1545
   ClientWidth     =   4275
   LinkTopic       =   "Form1"
   ScaleHeight     =   2475
   ScaleWidth      =   4275
   Begin VB.Timer Timer1 
      Interval        =   1000
      Left            =   3480
      Top             =   360
   End
   Begin VB.Label Label5 
      Caption         =   "Label1"
      BeginProperty Font 
         Name            =   "Arial"
         Size            =   12
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   375
      Left            =   120
      TabIndex        =   4
      Top             =   2040
      Width           =   4000
   End
   Begin VB.Label Label4 
      Caption         =   "Label1"
      BeginProperty Font 
         Name            =   "Arial"
         Size            =   12
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   375
      Left            =   120
      TabIndex        =   3
      Top             =   1560
      Width           =   4000
   End
   Begin VB.Label Label3 
      Caption         =   "Label1"
      BeginProperty Font 
         Name            =   "Arial"
         Size            =   12
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   375
      Left            =   120
      TabIndex        =   2
      Top             =   1080
      Width           =   4000
   End
   Begin VB.Label Label2 
      Caption         =   "Label1"
      BeginProperty Font 
         Name            =   "Arial"
         Size            =   12
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   375
      Left            =   120
      TabIndex        =   1
      Top             =   600
      Width           =   4000
   End
   Begin VB.Label Label1 
      Caption         =   "Label1"
      BeginProperty Font 
         Name            =   "Arial"
         Size            =   12
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   375
      Left            =   120
      TabIndex        =   0
      Top             =   120
      Width           =   4000
   End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Dim defaultTime As ITimeOfDay
Dim pacificTime As ITimeOfDay
Dim mountainTime As ITimeOfDay
Dim centralTime As ITimeOfDay
Dim easternTime As ITimeOfDay

Private Sub Form_Load()
  Set defaultTime = GetObject("clsid:8C54EFA2-B85F-11d0-8C3E-0080C73925BA")
  Set pacificTime = GetObject("clsid:8C54EFA2-B85F-11d0-8C3E-0080C73925BA:!pacific")
  Set mountainTime = GetObject("clsid:8C54EFA2-B85F-11d0-8C3E-0080C73925BA:!mountain")
  Set centralTime = GetObject("clsid:8C54EFA2-B85F-11d0-8C3E-0080C73925BA:!central")
  Set easternTime = GetObject("clsid:8C54EFA2-B85F-11d0-8C3E-0080C73925BA:!eastern")
End Sub

Private Sub Timer1_Timer()
  Label1.Caption = "Default Time: " & defaultTime.GetCurrentTimeOfDay & Chr(13)
  Label2.Caption = "Pacific Time: " & pacificTime.GetCurrentTimeOfDay & Chr(13)
  Label3.Caption = "Mountain Time: " & mountainTime.GetCurrentTimeOfDay & Chr(13)
  Label4.Caption = "Central Time: " & centralTime.GetCurrentTimeOfDay & Chr(13)
  Label5.Caption = "Eastern Time: " & easternTime.GetCurrentTimeOfDay & Chr(13)
End Sub