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