KOALA.CPP

/* 
* KOALA.CPP
* Koala Object with Custom Marshaling, Chapter 6
*
* Implementation of the CKoala object with a custom interface
* to demonstrate local/remote transparency.
*
* Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/


#include "koala.h"


/*
* CKoala::CKoala
* CKoala::~CKoala
*
* Parameters (Constructor):
* pUnkOuter LPUNKNOWN of a controlling unknown.
* pfnDestroy PFNDESTROYED to call when an object
* is destroyed.
*/

CKoala::CKoala(LPUNKNOWN pUnkOuter, PFNDESTROYED pfnDestroy)
{
m_cRef=0;
m_pUnkOuter=pUnkOuter;
m_pfnDestroy=pfnDestroy;

m_pImpIAnimal=NULL;
m_pImpIKoala=NULL;
m_pImpIMarshal=NULL;

m_fJustAte=FALSE;
m_cSleepAfterEat=0;

return;
}


CKoala::~CKoala(void)
{
DeleteInterfaceImp(m_pImpIMarshal);
DeleteInterfaceImp(m_pImpIKoala);
DeleteInterfaceImp(m_pImpIAnimal);
return;
}



/*
* CKoala::Init
*
* Purpose:
* Performs any intiailization of a CKoala that's prone to failure
* that we also use internally before exposing the object outside.
*
* Parameters:
* None
*
* Return Value:
* BOOL TRUE if the function is successful,
* FALSE otherwise.
*/

BOOL CKoala::Init(void)
{
IUnknown *pUnkOuter=m_pUnkOuter;

if (NULL==pUnkOuter)
pUnkOuter=this;

m_pImpIAnimal=new CImpIAnimal(this, pUnkOuter);

if (NULL==m_pImpIAnimal)
return FALSE;

m_pImpIKoala=new CImpIKoala(this, pUnkOuter);

if (NULL==m_pImpIKoala)
return FALSE;

m_pImpIMarshal=new CImpIMarshal(this, pUnkOuter);

if (NULL==m_pImpIMarshal)
return FALSE;

return TRUE;
}




/*
* CKoala::CallLocal
*
* Purpose:
* Sends or posts a message to the local object.
*
* Parameters:
* iMsg UINT identifying the function to call
* lParam LPARAM containing extra information
* fAsync BOOL indicating if this is a Post (TRUE) or
* a send (FALSE).
*
* Return Value:
* DWORD Return value from the function or an HRESULT
* on failure.
*/

DWORD CKoala::CallLocal(UINT iMsg, LPARAM lParam, BOOL fAsync)
{
DWORD dwRet=0;

if (fAsync)
PostMessage(m_hWndLocal, WM_COMMAND, (WPARAM)iMsg, lParam);
else
{
dwRet=SendMessage(m_hWndLocal, WM_COMMAND, (WPARAM)iMsg
, lParam);
}

return dwRet;
}




/*
* CKoala::QueryInterface
* CKoala::AddRef
* CKoala::Release
*
* Purpose:
* IUnknown members for CKoala object.
*/

STDMETHODIMP CKoala::QueryInterface(REFIID riid, PPVOID ppv)
{
*ppv=NULL;

/*
* The only calls for IUnknown are either in a nonaggregated
* case or when created in an aggregation, so in either case
* always return our IUnknown for IID_IUnknown.
*/
if (IID_IUnknown==riid)
*ppv=this;

if (IID_IAnimal==riid)
*ppv=m_pImpIAnimal;

if (IID_IKoala==riid)
*ppv=m_pImpIKoala;

if (IID_IMarshal==riid)
*ppv=m_pImpIMarshal;

if (NULL!=*ppv)
{
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}

return ResultFromScode(E_NOINTERFACE);
}


STDMETHODIMP_(ULONG) CKoala::AddRef(void)
{
return ++m_cRef;
}


STDMETHODIMP_(ULONG) CKoala::Release(void)
{
if (0L!=--m_cRef)
return m_cRef;

/*
* If this is the last Release, then we have to tell
* the server to free its object too. This is an async
* call as we don't need to hang around for it to quit.
*/
CallLocal(MSG_RELEASE, 0, TRUE);

if (NULL!=m_pfnDestroy)
(*m_pfnDestroy)();

delete this;
return 0;
}





//Proxy-side IAnimal implementation


/*
* CImpIAnimal::CImpIAnimal
* CImpIAnimal::~CImpIAnimal
*
* Constructor Parameters:
* pObj PCKoala of the object containing us.
* pUnkOuter LPUNKNOWN to which we blindly delegate
* all IUnknown calls.
*/

CImpIAnimal::CImpIAnimal(PCKoala pObj, LPUNKNOWN pUnkOuter)
{
m_cRef=0;
m_pObj=pObj;
m_pUnkOuter=pUnkOuter;
return;
}

CImpIAnimal::~CImpIAnimal(void)
{
return;
}



/*
* CImpIAnimal::QueryInterface
* CImpIAnimal::AddRef
* CImpIAnimal::Release
*
* Purpose:
* Delegating IUnknown members for interface implementation.
*/

STDMETHODIMP CImpIAnimal::QueryInterface(REFIID riid
, LPVOID *ppv)
{
return m_pUnkOuter->QueryInterface(riid, ppv);
}

STDMETHODIMP_(ULONG) CImpIAnimal::AddRef(void)
{
++m_cRef;
return m_pUnkOuter->AddRef();
}

STDMETHODIMP_(ULONG) CImpIAnimal::Release(void)
{
--m_cRef;
return m_pUnkOuter->Release();
}



/*
* CImpIAnimal::Eat
*
* Purpose:
* Instructs the animal to eat something, returning what the animal
* actually ate which usually goes against recommendation (which is
* true for the human animal, too).
*
* Parameters:
* pszFoodRecommended LPTSTR describing the food that the animal
* should eat.
* pszFoodEaten LPTSTR describing the food the animal actually
* ate, which may not, of course, be the same as
* what it should eat.
* cchEaten short containing the lenght of pszFoodEaten.
*
* Return Value:
* HRESULT NOERROR if food is eaten, S_FALSE if not.
*/

STDMETHODIMP CImpIAnimal::Eat(LPTSTR pszFoodRecommended
, LPTSTR pszFoodEaten, short cchEaten)
{
/*
* Koalas aren't don't care what you tell them, they eat one
* thing. We can handle the return string for the local
* object, but we still need to tell it that this was
* called.
*/

_tcsncpy(pszFoodEaten, TEXT("Eucalyptus Leaves"), cchEaten);
m_pObj->CallLocal(MSG_EAT, 0L, FALSE);
return NOERROR;
}



/*
* CImpIAnimal::Sleep
*
* Purpose:
* Instructs the animal to sleep for a while.
*
* Parameters:
* pcMinutes short * (in-out) containing the number of
* minutes to sleep on entry, then number of
* minutes actually slept on exit
*
* Return Value:
* HRESULT NOERROR if sleep taken, S_FALSE if not, where
* *pcMinutes should be zero.
*/

STDMETHODIMP CImpIAnimal::Sleep(short *pcMinutes)
{
DWORD dwRet;

//Pass the client's value
dwRet=m_pObj->CallLocal(MSG_SLEEP, (LPARAM)*pcMinutes, FALSE);

if (FAILED((HRESULT)dwRet))
return (HRESULT)dwRet;

//Store the return value in the client's variable.
*pcMinutes=LOWORD(dwRet);
return NOERROR;
}



/*
* CImpIAnimal::Procreate
*
* Purpose:
* Instructs the animal to procreate. On entry, the number of
* offstring is unknown, so that's an out parameter.
*
* Parameters:
* pcOffspring short * (out) in which to store the number
* of new offspring.
*
* Return Value:
* HRESULT NOERROR if offspring created, S_FALSE if not
* where *pcOffspring should be zero.
*/

STDMETHODIMP CImpIAnimal::Procreate(short *pcOffspring)
{
DWORD dwRet;

dwRet=m_pObj->CallLocal(MSG_PROCREATE, 0, FALSE);

if (FAILED((HRESULT)dwRet))
return (HRESULT)dwRet;

*pcOffspring=(short)LOWORD(dwRet);
return ResultFromScode(0==dwRet ? S_FALSE : S_OK);
}




/*
* CImpIAnimal::WhatKindOfAnimal
*
* Purpose:
* Returns the IID of the specific animal interface that describes
* the type of animal this really is (a much more complex
* classification scheme might have IAnimal::WhatGenus and
* IGenus::WhatSpecies, etc., but we're just being simple here).
*
* Parameters:
* pIID IID * in which to store the specific
* animal IID.
*
* Return Value:
* HRESULT NOERROR if the animal type is known,
* S_FALSE if not with *pIID set to IID_NULL.
*/

STDMETHODIMP CImpIAnimal::WhatKindOfAnimal(IID *pIID)
{
//No need to ask the local object for something we know
*pIID=IID_IKoala;
return NOERROR;
}





//IKoala implementation


/*
* CImpIKoala::CImpIKoala
* CImpIKoala::~CImpIKoala
*
* Constructor Parameters:
* pObj PCKoala of the object containing us.
* pUnkOuter LPUNKNOWN to which we blindly delegate
* all IUnknown calls.
*/

CImpIKoala::CImpIKoala(PCKoala pObj, LPUNKNOWN pUnkOuter)
{
m_cRef=0;
m_pObj=pObj;
m_pUnkOuter=pUnkOuter;
return;
}

CImpIKoala::~CImpIKoala(void)
{
return;
}



/*
* CImpIKoala::QueryInterface
* CImpIKoala::AddRef
* CImpIKoala::Release
*
* Purpose:
* Delegating IUnknown members for interface implementation.
*/

STDMETHODIMP CImpIKoala::QueryInterface(REFIID riid
, LPVOID *ppv)
{
return m_pUnkOuter->QueryInterface(riid, ppv);
}

STDMETHODIMP_(ULONG) CImpIKoala::AddRef(void)
{
++m_cRef;
return m_pUnkOuter->AddRef();
}

STDMETHODIMP_(ULONG) CImpIKoala::Release(void)
{
--m_cRef;
return m_pUnkOuter->Release();
}



/*
* CImpIKoala::ClimbEucalyptusTree
*
* Purpose:
* Tells the Koala to go climb a tree, which means eating, which
* a koala is probably more than happy to do.
*
* Parameters:
* iTree short identifying the tree to climb.
*
* Return Value:
* HRESULT NOERROR if tree climbed, S_FALSE if not.
*/

STDMETHODIMP CImpIKoala::ClimbEucalyptusTree(short iTree)
{
//We know the server doesn't need this
return NOERROR;
}



/*
* CImpIKoala::PouchOpensDown
*
* Purpose:
* Do-nothing function to demonstrate a void argument list.
*
* Parameters:
* None
*
* Return Value:
* HRESULT NOERROR
*/

STDMETHODIMP CImpIKoala::PouchOpensDown(void)
{
//We know the server doesn't need this
return NOERROR;
}



/*
* CImpIKoala::SleepAfterEating
*
* Purpose:
* Tells the Koala to sleep an additional number of minutes after
* eating.
*
* Parameters:
* cMinutes short * (in) containing the number of
* extra minutes to sleep after eating.
*
* Return Value:
* HRESULT NOERROR
*/

STDMETHODIMP CImpIKoala::SleepAfterEating(short cMinutes)
{
DWORD dwRet;

dwRet=m_pObj->CallLocal(MSG_SLEEPAFTEREATING
, (LPARAM)cMinutes, TRUE);

if (FAILED((HRESULT)dwRet))
return (HRESULT)dwRet;

return NOERROR;
}