Figure 1    Time Server

TimeOfDay.h

class CTimeOfDay : public ITimeOfDay
{
public:
    // IUnknown

    STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

    // ITimeOfDay

    STDMETHOD(GetDateString)(/*[out, retval]*/ BSTR *pVal);
    STDMETHOD(GetTimeString)(/*[out, retval]*/ BSTR *pVal);

    CTimeOfDay();
    ~CTimeOfDay();

private:
    long m_lRefCount;
};
TimeOfDay.cpp
#include "stdafx.h"
#include "TimeSvr.h"
#include "TimeOfDay.h"

extern long g_lObjCount;

CTimeOfDay::CTimeOfDay()
: m_lRefCount(1)
{ 
    InterlockedIncrement(&g_lObjCount); 
}

CTimeOfDay::~CTimeOfDay()
{ 
    InterlockedDecrement(&g_lObjCount); 
}

STDMETHODIMP CTimeOfDay::QueryInterface(const IID& iid, void** ppv)
{    
    if ((iid == IID_IUnknown) || (iid == IID_ITimeOfDay))
    {
        *ppv = static_cast<ITimeOfDay*>(this); 
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK ;
}

STDMETHODIMP_(ULONG) CTimeOfDay::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(ULONG) CTimeOfDay::Release() 
{
    if (InterlockedDecrement(&m_lRefCount) == 0)
    {
        delete this;
        return 0;
    }

    return m_lRefCount;
}

STDMETHODIMP CTimeOfDay::GetTimeString(BSTR* pVal)
{
    int nSize = GetTimeFormat(NULL, 0, NULL, NULL, NULL, 0);
    TCHAR* sTimeBuffer = new TCHAR[nSize];
    GetTimeFormat(NULL, 0, NULL, NULL, sTimeBuffer, nSize);

    *pVal = _bstr_t(sTimeBuffer).copy();
    delete [] sTimeBuffer;
    return S_OK;
}

STDMETHODIMP CTimeOfDay::GetDateString(BSTR* pVal)
{
    int nSize = GetDateFormat(NULL, DATE_LONGDATE, NULL, NULL, NULL, 0);
    TCHAR* sDateBuffer = new TCHAR[nSize];
    GetDateFormat(NULL, DATE_LONGDATE, NULL, NULL, sDateBuffer, nSize);

    *pVal = _bstr_t(sDateBuffer).copy();
    delete [] sDateBuffer;
    return S_OK;
}
Factory.cpp

#include "stdafx.h"
#include "TimeSvr.h"
#include "TimeOfDay.h"
#include "Factory.h"

long CTimeSvrFactory::m_lLockCount = 0;

STDMETHODIMP CTimeSvrFactory::QueryInterface(const IID& iid, void** ppv)
{    
    if ((iid == IID_IUnknown) || (iid == IID_IClassFactory))
    {
        *ppv = static_cast<IClassFactory*>(this); 
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }
    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
    return S_OK ;
}

STDMETHODIMP_(ULONG) CTimeSvrFactory::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(ULONG) CTimeSvrFactory::Release() 
{
    if (InterlockedDecrement(&m_lRefCount) == 0)
    {
        delete this;
        return 0;
    }

    return m_lRefCount;
}

STDMETHODIMP CTimeSvrFactory::CreateInstance(
    IUnknown* pUnknownOuter, const IID& iid, void** ppv) 
{
    if (pUnknownOuter != NULL)
        return CLASS_E_NOAGGREGATION;

    CTimeOfDay* pObject = new CTimeOfDay;
    if (pObject == NULL)
        return E_OUTOFMEMORY;

    HRESULT hr = pObject->QueryInterface(iid, ppv);
    pObject->Release();
    return hr;
}

STDMETHODIMP CTimeSvrFactory::LockServer(BOOL bLock) 
{
    if (bLock)
        InterlockedIncrement(&m_lLockCount);
    else
        InterlockedDecrement(&m_lLockCount);

    return S_OK;
}
TimeSvr.cpp

#include "stdafx.h"
#include "resource.h"
#include "Factory.h"

#include "TimeSvr.h"
#include "TimeSvr_i.c"

long g_lObjCount = 0;
static HINSTANCE g_hModule;

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
{
    if (dwReason == DLL_PROCESS_ATTACH)
         g_hModule = hInstance;

    return TRUE;
}

STDAPI DllCanUnloadNow(void)
{
    return (g_lObjCount == 0) ? S_OK : S_FALSE;
}


STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    if (rclsid != CLSID_TimeOfDay)
        return CLASS_E_CLASSNOTAVAILABLE;

    // Create class factory.

    CTimeSvrFactory* pFactory = new CTimeSvrFactory;
    if (pFactory == NULL)
        return E_OUTOFMEMORY;

    // Get requested interface.

    HRESULT hr = pFactory->QueryInterface(riid, ppv);
    pFactory->Release();

    return hr;
}


BOOL SetRegistryValue(LPCTSTR szKey, LPCTSTR szSubkey, LPCTSTR szValue)
{
    HKEY hKey;
    TCHAR szKeyBuf[1024];

    lstrcpy(szKeyBuf, szKey);
    if (szSubkey != NULL)
    {
        lstrcat(szKeyBuf, _T("\\"));
        lstrcat(szKeyBuf, szSubkey);
    }

    long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, szKeyBuf, 0, NULL, 0,
                                  KEY_ALL_ACCESS, NULL, &hKey, NULL);
    if (lResult != ERROR_SUCCESS)
        return FALSE;

    if (szValue != NULL)
    {
        RegSetValueEx(hKey, NULL, 0, REG_SZ,(BYTE*)szValue, 
                      lstrlen(szValue)+1);
    }

    RegCloseKey(hKey);
    return TRUE;
}


STDAPI DllRegisterServer(void)
{
    // Get the filename of our COM server

    TCHAR* szModule = new TCHAR[_MAX_PATH];
    GetModuleFileName(g_hModule, szModule, _MAX_PATH);

    // Get the class name string for TimeOfDay class

    LPOLESTR wszCLSID = NULL;
    StringFromCLSID(CLSID_TimeOfDay, &wszCLSID);
    int nLen = lstrlenW(wszCLSID);

    TCHAR* szCLSID = new TCHAR[nLen + 1];

#ifndef _UNICODE
    wcstombs(szCLSID, wszCLSID, nLen);
    szCLSID[nLen] = 0;
#else
    lstrcpy(szCLSID, wszCLSID);
#endif
 
   CoTaskMemFree(wszCLSID);

    // Register the TimeOfDay server
    
    TCHAR* szKey = new TCHAR[_MAX_PATH];
    lstrcpy(szKey, _T("CLSID\\"));
    lstrcat(szKey, szCLSID);

    SetRegistryValue(szKey, NULL, _T("TimeOfDay Server"));
    SetRegistryValue(szKey, _T("InprocServer32"), szModule);
    SetRegistryValue(szKey, _T("ProgID"), _T("ExtremeCPP.TimeOfDay.1"));
    SetRegistryValue(szKey, _T("VersionIndependentProgID"),
                     _T("ExtremeCPP.TimeOfDay"));

    SetRegistryValue(_T("ExtremeCPP.TimeOfDay"), NULL, _T("TimeOfDay Server"));
    SetRegistryValue(_T("ExtremeCPP.TimeOfDay"), _T("CLSID"), szCLSID);
    SetRegistryValue(_T("ExtremeCPP.TimeOfDay"), _T("CurVer"),
                     _T("ExtremeCPP.TimeOfDay.1"));

    SetRegistryValue(_T("ExtremeCPP.TimeOfDay.1"), "CLSID", szCLSID);
    SetRegistryValue(_T("ExtremeCPP.TimeOfDay.1"), NULL,
                     _T("TimeOfDay Server")); 

    delete [] szKey;
    delete [] szCLSID;
    delete [] szModule;
    return S_OK;
}


LONG RecursiveDeleteKey(HKEY hKeyParent, LPCTSTR lpszKeyChild)
{
    HKEY hKeyChild;
    LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyChild, 0,
                             KEY_ALL_ACCESS, &hKeyChild);
    if (lRes != ERROR_SUCCESS)
        return lRes;

    char szBuffer[_MAX_PATH];
    DWORD dwSize = _MAX_PATH;
    while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
           NULL, NULL, NULL) == S_OK)
    {
        lRes = RecursiveDeleteKey(hKeyChild, szBuffer);
        if (lRes != ERROR_SUCCESS)
        {
            RegCloseKey(hKeyChild);
            return lRes;
        }
        
        dwSize = _MAX_PATH;
    }

    RegCloseKey(hKeyChild);
    return RegDeleteKey(hKeyParent, lpszKeyChild);
}


STDAPI DllUnregisterServer(void)
{
    // Get the class name string for TimeOfDay class

    LPOLESTR wszCLSID = NULL;
    StringFromCLSID(CLSID_TimeOfDay, &wszCLSID);
    int nLen = lstrlenW(wszCLSID);

    TCHAR* szCLSID = new TCHAR[nLen + 1];

#ifndef _UNICODE
    wcstombs(szCLSID, wszCLSID, nLen);
    szCLSID[nLen] = 0;
#else
    lstrcpy(szCLSID, wszCLSID);
#endif
  
  CoTaskMemFree(wszCLSID);

    // Unregister the TimeOfDay server
    
    TCHAR* szKey = new TCHAR[_MAX_PATH];
    lstrcpy(szKey, _T("CLSID\\"));
    lstrcat(szKey, szCLSID);

    RecursiveDeleteKey(HKEY_CLASSES_ROOT, szKey);
    RecursiveDeleteKey(HKEY_CLASSES_ROOT, _T("ExtremeCPP.TimeOfDay"));
    RecursiveDeleteKey(HKEY_CLASSES_ROOT, _T("ExtremeCPP.TimeOfDay.1"));

    delete [] szKey;
    delete [] szCLSID;
    return S_OK;
}

Figure 2   Time Server in ATL

TimeOfDay.h


 #ifndef __TIMEOFDAY_H_
 #define __TIMEOFDAY_H_
 
 #include "resource.h"       // main symbols
 
 /////////////////////////////////////////////////////////////////////////////
 // CTimeOfDay
 class ATL_NO_VTABLE CTimeOfDay : 
     public CComObjectRootEx<CComSingleThreadModel>,
     public CComCoClass<CTimeOfDay, &CLSID_TimeOfDay>,
     public IDispatchImpl<ITimeOfDay, &IID_ITimeOfDay, &LIBID_TIMESVRLib>
 {
 public:
     CTimeOfDay()
     {
     }
 
 DECLARE_REGISTRY_RESOURCEID(IDR_TIMEOFDAY)
 
 BEGIN_COM_MAP(CTimeOfDay)
     COM_INTERFACE_ENTRY(ITimeOfDay)
     COM_INTERFACE_ENTRY(IDispatch)
 END_COM_MAP()
 
 // ITimeOfDay
 public:
     STDMETHOD(get_DateString)(/*[out, retval]*/ BSTR *pVal);
     STDMETHOD(get_TimeString)(/*[out, retval]*/ BSTR *pVal);
 };
 
 #endif //__TIMEOFDAY_H_
TimeSvr.cpp

 #include "stdafx.h"
 #include "resource.h"
 #include "initguid.h"
 #include "TimeSvr.h"
 
 #include "TimeSvr_i.c"
 #include "TimeOfDay.h"
 
 
 CComModule _Module;
 
 BEGIN_OBJECT_MAP(ObjectMap)
     OBJECT_ENTRY(CLSID_TimeOfDay, CTimeOfDay)
 END_OBJECT_MAP()
 
 extern "C"
 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
 {
     if (dwReason == DLL_PROCESS_ATTACH)
     {
         _Module.Init(ObjectMap, hInstance);
         DisableThreadLibraryCalls(hInstance);
     }
     else if (dwReason == DLL_PROCESS_DETACH)
         _Module.Term();
     return TRUE;    // ok
 }
 
 STDAPI DllCanUnloadNow(void)
 {
     return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
 }
 
 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
 {
     return _Module.GetClassObject(rclsid, riid, ppv);
 }
 
 STDAPI DllRegisterServer(void)
 {
     // registers object, typelib and all interfaces in typelib
     return _Module.RegisterServer(TRUE);
 }
 
 STDAPI DllUnregisterServer(void)
 {
     _Module.UnregisterServer();
     return S_OK;
 }
TimeSvr.idl

 import "oaidl.idl";
 import "ocidl.idl";
 
     [
         object,
         uuid(3AF29C10-0853-11D1-9A27-00A0C91EF7B9),
         dual,
         helpstring("ITimeOfDay Interface"),
         pointer_default(unique)
     ]
     interface ITimeOfDay : IDispatch
     {
         [propget, id(1), helpstring("property TimeString")] HRESULT TimeString([out,
                                                                 retval] BSTR *pVal);
         [propget, id(2), helpstring("property DateString")] HRESULT DateString([out,
                                                                 retval] BSTR *pVal);
     };
 [
     uuid(3AF29C03-0853-11D1-9A27-00A0C91EF7B9),
     version(1.0),
     helpstring("TimeSvr 1.0 Type Library")
 ]
 library TIMESVRLib
 {
     importlib("stdole32.tlb");
     importlib("stdole2.tlb");
 
     [
         uuid(3AF29C11-0853-11D1-9A27-00A0C91EF7B9),
         helpstring("TimeOfDay Class")
     ]
     coclass TimeOfDay
     {
         [default] interface ITimeOfDay;
     };
 };

Figure 3    New COM+ Services

New Service Description Disadvantage
Class Exposure COM+ will allow you to directly expose classes in addition to traditional interfaces, making it easier to share objects. Interfaces are a rich, effective way of separating behavior from implementation. Exposing classes directly will tend to increase the coupling between client and server. Furthermore, class references will only be understood by applications and tools that support COM+, meaning that legacy applications will have to be rebuilt to be able to import object references.
Implementation Inheritance COM+ will allow you to subclass the functionality—not just the interfaces—of directly exposed classes. You'll be able to extend COM+ objects easily using inheritance instead of aggregation. Even though the lack of implementation inheritance is one of the most widely criticized "limitations" in COM, there is a good reason for it. Without a complete documentation for an object—its source code, in other words—it's nearly impossible to determine how to safely override that object's behavior. Since a derived class must learn some of the details of the base class to accurately augment its behavior, implementation inheritance tends to increase the coupling between objects.
Extended Security COM+ will provide a security model that extends many of the features provided by COM, MTS, and the Microsoft Java Virtual Machine. You'll be able to define role-based security privileges to your components and let administrators decide which users to assign to those roles. It is unclear whether existing COM objects will be able to take advantage of the extended security model. The COM+ services will impose runtime overhead.
Garbage Collection COM+ will handle reference counting, so you won't have to worry about making paired calls to AddRef and Release to manage the lifetime of objects. COM+ will release objects automatically when it determines that they are no longer in use. A garbage collection algorithm—not the client—will ultimately determine the lifetime of an object. As a result, an object might actually stay in memory long after its reference count has dropped to zero. This will require you to consider race conditions—the indeterminate order in which two or more objects are freed—and resource allocation issues.