IOLECONT.CPP
/* 
 * IOLECONT.CPP 
 * Patron Chapter 24 
 * 
 * Implementation of the IOleItemContainer interface for Patron's 
 * CPage and CPatronDoc alike, using the constructor parameter fDoc 
 * to differentiate. 
 * 
 * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved 
 * 
 * Kraig Brockschmidt, Microsoft 
 * Internet  :  kraigb@microsoft.com 
 * Compuserve:  >INTERNET:kraigb@microsoft.com 
 */ 
 
 
#include <stdlib.h> 
#include "patron.h" 
 
 
/* 
 * CImpIOleItemContainer::CImpIOleItemContainer 
 * CImpIOleItemContainer::~CImpIOleItemContainer 
 * 
 * Parameters (Constructor): 
 *  pObj            LPVOID of the page or pages. 
 *  pUnkOuter       LPUNKNOWN to which we delegate. 
 *  fDoc            BOOL indicating if we're in CPatronDoc or CPage 
 */ 
 
CImpIOleItemContainer::CImpIOleItemContainer(LPVOID pObj 
    , LPUNKNOWN pUnkOuter, BOOL fDoc) 
    { 
    m_cRef=0; 
    m_fDoc=fDoc; 
 
    if (fDoc) 
        { 
        m_pDoc=(PCPatronDoc)pObj; 
        m_pPage=NULL; 
        } 
    else 
        { 
        m_pDoc=NULL; 
        m_pPage=(PCPage)pObj; 
        } 
 
    m_pUnkOuter=pUnkOuter; 
    return; 
    } 
 
CImpIOleItemContainer::~CImpIOleItemContainer(void) 
    { 
    return; 
    } 
 
 
 
 
/* 
 * CImpIOleItemContainer::QueryInterface 
 * CImpIOleItemContainer::AddRef 
 * CImpIOleItemContainer::Release 
 * 
 * Purpose: 
 *  IUnknown members for CImpIOleItemContainer object. 
 */ 
 
STDMETHODIMP CImpIOleItemContainer::QueryInterface(REFIID riid 
    , PPVOID ppv) 
    { 
    return m_pUnkOuter->QueryInterface(riid, ppv); 
    } 
 
 
STDMETHODIMP_(ULONG) CImpIOleItemContainer::AddRef(void) 
    { 
    ++m_cRef; 
    return m_pUnkOuter->AddRef(); 
    } 
 
STDMETHODIMP_(ULONG) CImpIOleItemContainer::Release(void) 
    { 
    --m_cRef; 
    return m_pUnkOuter->Release(); 
    } 
 
 
 
/* 
 * CImpIOleItemContainer::ParseDisplayName 
 * 
 * Purpose: 
 *  Inherited member of IParseDisplayName that takes a string name 
 *  and turns out a moniker for it. 
 * 
 * Parameters: 
 *  pbc             LPBC to the binding context 
 *  pszName         LPOLESTR to the name to parse. 
 *  pchEaten        ULONG * into which to store how many 
 *                  characters we scanned in the display name. 
 *  ppmk            LPMONIKER * in which to return the moniker. 
 * 
 * Return Value: 
 *  HRESULT         NOERROR or a general error value. 
 */ 
 
STDMETHODIMP CImpIOleItemContainer::ParseDisplayName(LPBC pbc 
    , LPOLESTR pszName, ULONG *pchEaten, LPMONIKER *ppmk) 
    { 
    OLECHAR     ch; 
    ULONG       chEaten=0; 
    TCHAR       szName[256]; 
    TCHAR       szComp[15]; 
    LPTSTR      psz; 
    UINT        cch; 
 
    *ppmk=NULL; 
    *pchEaten=0; 
 
    /* 
     * All we have to look for is the string between the ! 
     * delimeters (or a null terminator).  pszName should be pointing 
     * to a !, so skip it and scan the string for a ! or 0, 
     * then pass the result to CreateItemMoniker. 
     */ 
 
    psz=szName; 
 
    ch=*pszName++; 
    chEaten++; 
 
    if ((OLECHAR)'!'!=ch) 
        return ResultFromScode(MK_E_SYNTAX); 
 
    ch=*pszName++; 
 
    while ((OLECHAR)0!=ch && (OLECHAR)'!' !=ch) 
        { 
        *psz++=(TCHAR)ch; 
        chEaten++; 
        ch=*pszName++; 
        } 
 
    *psz=(TCHAR)0; 
 
    /* 
     * Syntax check.  If we're the DOcument object, check for 
     * "Page n" at the beginning of the string.  Otherwise check 
     * for "Tenant n". 
     */ 
    lstrcpy(szComp, m_fDoc ? TEXT("Page ") : TEXT("Tenant ")); 
 
    //Does szName start with szComp? 
    cch=lstrlen(szComp); 
 
    if (0!=_tcsncicmp(szName, szComp, cch)) 
        { 
        *pchEaten=1;    //Parsed ! at least 
        return ResultFromScode(MK_E_SYNTAX); 
        } 
 
    //Check for a number in szName 
    if ((TCHAR)'0' != szName[cch]) 
        { 
        if (0==_ttoi(szName+cch)) 
            { 
            *pchEaten=cch;  //Got past name 
            return ResultFromScode(MK_E_SYNTAX); 
            } 
        } 
 
    *pchEaten=chEaten; 
   #ifdef WIN32ANSI 
    //Use the ANSI version here since szName is ANSI 
    return INOLE_CreateItemMoniker(TEXT("!"), szName, ppmk); 
   #else 
    return CreateItemMoniker(OLETEXT("!"), szName, ppmk); 
   #endif 
    } 
 
 
 
 
/* 
 * CImpIOleItemContainer::EnumObjects 
 * 
 * Purpose: 
 *  Creates and returns an IEnumUnknown object that allows the 
 *  caller to walk through the objects in this continer thing. 
 * 
 * Parameters: 
 *  dwFlags         DWORD specifying what kind of objects to 
 *                  enumerate. 
 *  ppEnum          LPENUMUNKNOWN * into which to return the 
 *                  enumerator 
 * 
 * Return Value: 
 *  HRESULT         NOERROR or a general error value. 
 */ 
 
STDMETHODIMP CImpIOleItemContainer::EnumObjects(DWORD dwFlags 
    , LPENUMUNKNOWN *ppEnum) 
    { 
    *ppEnum=NULL; 
    return ResultFromScode(E_NOTIMPL); 
    } 
 
 
 
 
/* 
 * CImpIOleItemContainer::LockContainer 
 * 
 * Purpose: 
 *  Establishes a lock on the container to prevent it from shutting 
 *  down outside of user control.  This is used to control the 
 *  lifetime of the container when it's used to update a link to an 
 *  embedded object within it.  If we're unlock and the user has not 
 *  taken control, we close. 
 * 
 * Parameters: 
 *  fLock           BOOL indicating a lock or unlock. 
 * 
 * Return Value: 
 *  HRESULT         NOERROR or a general error value. 
 */ 
 
STDMETHODIMP CImpIOleItemContainer::LockContainer(BOOL fLock) 
    { 
    /* 
     * This is pretty much the same implementation as 
     * IClassFactory::LockServer, and we can use the same lock 
     * count to accomplish our goal. 
     */ 
 
    if (fLock) 
        g_cLock++; 
    else 
        { 
        g_cLock--; 
        g_cObj++; 
        ObjectDestroyed(); 
        } 
 
    return NOERROR; 
    } 
 
 
 
 
 
 
/* 
 * CImpIOleItemContainer::GetObject 
 * 
 * Purpose: 
 *  Returns the requested interface pointer on an object in this 
 *  container. 
 * 
 * Parameters: 
 *  pszItem         LPOLESTR to the item we must locate. 
 *  dwSpeed         DWORD identifying how long the caller is willing 
 *                  to wait. 
 *  pcb             LPBINDCTX providing the binding context. 
 *  riid            REFIID of the interface requested. 
 *  ppv             PPVOID into which to return the object. 
 * 
 * Return Value: 
 *  HRESULT         NOERROR or a general error value. 
 */ 
 
STDMETHODIMP CImpIOleItemContainer::GetObject(LPOLESTR pszItem 
    , DWORD dwSpeed, LPBINDCTX pbc, REFIID riid, PPVOID ppv) 
    { 
    DWORD       dw; 
    char        szTemp[40];     //ANSI for atol 
    HRESULT     hr=ResultFromScode(E_FAIL); 
    PCPage      pPage; 
    PCTenant    pTenant; 
    LPUNKNOWN   pObj; 
    UINT        i, iCur; 
 
    *ppv=NULL; 
 
    if (m_fDoc) 
        { 
        /* 
         * The item name should be "Page n", so we'll do it the 
         * easy way:  call atol on pszItem+5 (we always know that 
         * we'll have "Page " there since we put it there (see 
         * CPage::GetStorageName). 
         */ 
 
        UNICODETOANSI((pszItem+5), szTemp, sizeof(szTemp)); 
        dw=atol(szTemp); 
 
        i=m_pDoc->m_pPG->IPageGetFromID(dw, &pPage, FALSE); 
 
        if (NOVALUE==i) 
            return hr; 
 
        /* 
         * If we're asked for immediate speed, only do this if the 
         * page is already current, i.e. everything is loaded. 
         */ 
        iCur=m_pDoc->m_pPG->CurPageGet(); 
 
        if (BINDSPEED_IMMEDIATE==dwSpeed && iCur!=i) 
            return ResultFromScode(MK_E_EXCEEDEDDEADLINE); 
 
        m_pDoc->m_pPG->CurPageSet(i); 
 
        //This will have changed to be the current page now. 
        if (NULL!=m_pDoc->m_pPG->m_pPageCur) 
            hr=m_pDoc->m_pPG->m_pPageCur->QueryInterface(riid, ppv); 
        } 
    else 
        { 
        if (TenantFromName(pszItem, &pTenant)) 
            { 
            pTenant->ObjectGet(&pObj); 
 
            /* 
             * If we're asked for immediate or moderate, only work 
             * if the object is already running. 
             */ 
            hr=IsRunning(pszItem);  //This is the function below 
 
            if ((BINDSPEED_IMMEDIATE==dwSpeed 
                || BINDSPEED_MODERATE==dwSpeed) && NOERROR!=hr) 
                hr=ResultFromScode(MK_E_EXCEEDEDDEADLINE); 
            else 
                { 
                //IMPORTANT:  Make sure this object is running first 
                OleRun(pObj); 
                hr=pObj->QueryInterface(riid, ppv); 
                } 
 
            pObj->Release(); 
            } 
        else 
            hr=ResultFromScode(MK_E_NOOBJECT); 
        } 
 
    return hr; 
    } 
 
 
 
 
 
 
/* 
 * CImpIOleItemContainer::GetObjectStorage 
 * 
 * Purpose: 
 *  Similar to get Object in that we have to locate the object 
 *  described by a given name, but instead of returning any old 
 *  interface we return a storage element. 
 * 
 * Parameters: 
 *  pszItem         LPOLESTR to the item we must locate. 
 *  pcb             LPBINDCTX providing the binding context. 
 *  riid            REFIID of the interface requested.  Usually 
 *                  IStorage or IStream. 
 *  ppv             PPVOID into which to return the object. 
 * 
 * Return Value: 
 *  HRESULT         NOERROR or a general error value. 
 */ 
 
STDMETHODIMP CImpIOleItemContainer::GetObjectStorage(LPOLESTR pszItem 
    , LPBINDCTX pbc, REFIID riid, PPVOID ppv) 
    { 
    PCTenant    pTenant; 
 
    *ppv=NULL; 
 
    if (m_fDoc) 
        return ResultFromScode(E_NOTIMPL); 
 
    //Can only handle IStorage. 
    if (IID_IStorage!=riid) 
        return ResultFromScode(E_NOINTERFACE); 
 
    if (TenantFromName(pszItem, &pTenant)) 
        pTenant->StorageGet((LPSTORAGE *)ppv); 
 
    return (NULL!=*ppv) ? NOERROR : ResultFromScode(E_FAIL); 
    } 
 
 
 
 
 
 
/* 
 * CImpIOleItemContainer::IsRunning 
 * 
 * Purpose: 
 *  Answers if the object under the given name is currently running. 
 * 
 * Parameters: 
 *  pszItem         LPOLESTR of the item to check 
 * 
 * Return Value: 
 *  HRESULT         NOERROR if the object is running, S_FALSE 
 *                  otherwise.  Possibly MK_E_NOOBJECT if the name 
 *                  is bogus. 
 */ 
 
STDMETHODIMP CImpIOleItemContainer::IsRunning(LPOLESTR pszItem) 
    { 
    HRESULT     hr; 
    PCTenant    pTenant; 
    LPUNKNOWN   pObj; 
    LPOLEOBJECT pIOleObject; 
 
    /* 
     * If this is the document's container interface, the object 
     * is a page and the page is always running. 
     */ 
    if (m_fDoc) 
        return NOERROR; 
    else 
        { 
        if (TenantFromName(pszItem, &pTenant)) 
            { 
            //Ask the actual object if its running. 
            pTenant->ObjectGet(&pObj); 
            hr=pObj->QueryInterface(IID_IOleObject 
                , (PPVOID)&pIOleObject); 
            pObj->Release(); 
 
            if (SUCCEEDED(hr)) 
                { 
                hr=(OleIsRunning(pIOleObject)) 
                    ? NOERROR : ResultFromScode(S_FALSE); 
                pIOleObject->Release(); 
                } 
            } 
        else 
            hr=ResultFromScode(MK_E_NOOBJECT); 
        } 
 
    return hr; 
    } 
 
 
 
 
/* 
 * CImpIOleItemContainer::TenantFromName 
 * (Private) 
 * 
 * Purpose: 
 *  This function which is NOT part of the interface retrieves 
 *  a tenant pointer from a tenant name. 
 * 
 * Parameters: 
 *  pszItem         LPOLESTR of the tenant to locate. 
 *  ppTenant        PCTenant * in which to return the pointer. 
 * 
 * Return Value: 
 *  BOOL            TRUE if successful, FALSE otherwise. 
 */ 
 
BOOL CImpIOleItemContainer::TenantFromName(LPOLESTR pszItem 
    , PCTenant *ppTenant) 
    { 
    DWORD       dw; 
    char        szTemp[40];     //ANSI for atol 
 
    if (m_fDoc) 
        return FALSE; 
 
    //The item name should be "Tenant xxxx", so use pszItem+7. 
    UNICODETOANSI((pszItem+7), szTemp, sizeof(szTemp)); 
    dw=atol(szTemp); 
 
    *ppTenant=NULL; 
 
    return m_pPage->TenantGetFromID(dw, ppTenant, FALSE); 
    }