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 ();
}