KOALA.CPP
/* 
 * KOALA.CPP 
 * Koala Object with Custom Interface, 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; 
 
    //CHAPTER6MOD 
    m_pImpIAnimal=NULL; 
    m_pImpIKoala=NULL; 
 
    m_fJustAte=FALSE; 
    m_cSleepAfterEat=0; 
    //End CHAPTER6MOD 
 
    return; 
    } 
 
 
CKoala::~CKoala(void) 
    { 
    //CHAPTER6MOD 
    DeleteInterfaceImp(m_pImpIKoala); 
    DeleteInterfaceImp(m_pImpIAnimal); 
    //End CHAPTER6MOD 
    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) 
    { 
    //CHAPTER6MOD 
    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; 
    //End CHAPTER6MOD 
 
    return TRUE; 
    } 
 
 
 
 
/* 
 * 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; 
 
    //CHAPTER6MOD 
    if (IID_IAnimal==riid) 
        *ppv=m_pImpIAnimal; 
 
    if (IID_IKoala==riid) 
        *ppv=m_pImpIKoala; 
    //End CHAPTER6MOD 
 
    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 (NULL!=m_pfnDestroy) 
        (*m_pfnDestroy)(); 
 
    delete this; 
    return 0; 
    } 
 
 
 
 
//CHAPTER6MOD 
 
//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 
   #ifdef WIN32ANSI 
    lstrcpyn(pszFoodEaten, "Eucalyptus Leaves", cchEaten); 
   #else 
    _tcsncpy(pszFoodEaten, TEXT("Eucalyptus Leaves"), cchEaten); 
   #endif 
    m_pObj->m_fJustAte=TRUE; 
    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) 
    { 
    /* 
     * Koalas welcome the opportunity to sleep, and will probably 
     * sleep longer than you tell them, so return more time than 
     * we were told based on IKoala::SleepAfterEating. 
     */ 
    *pcMinutes=*pcMinutes+m_pObj->m_cSleepAfterEat; 
    m_pObj->m_fJustAte=FALSE;     //Probably want to eat again 
    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       dw; 
    SCODE       sc=S_FALSE; 
 
    /* 
     * Koalas like to reproduce when they damn well please and 
     * not when someone else tells them, so we'll just check 
     * the current time, and if it's appropriate, we'll reproduce. 
     * (Simulation:  use GetTickCount to get the system time, then 
     * divide by 100 to filter out resolution concerns, then see if 
     * the result is an even multiple of 10). 
     * 
     * Also, Koalas only produce one offspring at a time.  If 
     * this was a rabbit, we might get anywhere from 2-20 offspring. 
     */ 
 
    dw=GetTickCount()/100; 
 
    if ((dw/10)*10==dw) 
        { 
        *pcOffspring=1; 
        sc=S_OK; 
        } 
    else 
        *pcOffspring=0; 
 
    return ResultFromScode(sc); 
    } 
 
 
 
 
/* 
 * 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) 
    { 
    *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) 
    { 
    //Nothing to demonstrate... 
    return NOERROR; 
    } 
 
 
 
/* 
 * CImpIKoala::PouchOpensDown 
 * 
 * Purpose: 
 *  Do-nothing function to demonstrate a void argument list. 
 * 
 * Parameters: 
 *  None 
 * 
 * Return Value: 
 *  HRESULT             NOERROR 
 */ 
 
STDMETHODIMP CImpIKoala::PouchOpensDown(void) 
    { 
    //Nothing to demonstrate... 
    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) 
    { 
    m_pObj->m_cSleepAfterEat=cMinutes; 
    return NOERROR; 
    } 
 
//End CHAPTER6MOD