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