home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / inole2 / chap09 / linksrc / iolecont.cpp < prev    next >
C/C++ Source or Header  |  1995-05-03  |  15KB  |  566 lines

  1. /*
  2.  * IOLECONT.CPP
  3.  * Link Source Server Chapter 9
  4.  *
  5.  * Implementation of the IOleItemContainer interface for
  6.  * LinkSource's CFileObject and CContainerItem, using constructor
  7.  * arguments to distinguish the relevant object.
  8.  *
  9.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  10.  *
  11.  * Kraig Brockschmidt, Microsoft
  12.  * Internet  :  kraigb@microsoft.com
  13.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  14.  */
  15.  
  16.  
  17. #include "linksrc.h"
  18.  
  19.  
  20. #ifdef WIN32ANSI
  21. /*
  22.  * This is to turn off the mapping to ANSI wrapper APIs because
  23.  * we're actually using wide char strings under Win32 all the time
  24.  * in parts of this code.
  25.  */
  26. #undef CreateItemMoniker
  27. #define CreateItemMoniker CreateItemMoniker
  28.  
  29. #endif
  30.  
  31. extern ULONG g_cObj;
  32.  
  33.  
  34. /*
  35.  * CImpIOleItemContainer::CImpIOleItemContainer
  36.  * CImpIOleItemContainer::~CImpIOleItemContainer
  37.  *
  38.  * Parameters (Constructor):
  39.  *  pObj            LPVOID of the page or pages.
  40.  *  pUnkOuter       LPUNKNOWN to which we delegate.
  41.  *  fFileObj        BOOL indicating if we're in CFileObject.
  42.  */
  43.  
  44. CImpIOleItemContainer::CImpIOleItemContainer(LPVOID pObj
  45.     , LPUNKNOWN pUnkOuter, BOOL fFileObj)
  46.     {
  47.     m_cRef=0;
  48.     m_fFileObj=fFileObj;
  49.  
  50.     if (fFileObj)
  51.         {
  52.         m_pObjFile=(PCFileObject)pObj;
  53.         m_pObjCont=NULL;
  54.         }
  55.     else
  56.         {
  57.         m_pObjFile=NULL;
  58.         m_pObjCont=(PCContainerItem)pObj;
  59.         }
  60.  
  61.     m_pUnkOuter=pUnkOuter;
  62.     return;
  63.     }
  64.  
  65. CImpIOleItemContainer::~CImpIOleItemContainer(void)
  66.     {
  67.     return;
  68.     }
  69.  
  70.  
  71.  
  72.  
  73. /*
  74.  * CImpIOleItemContainer::QueryInterface
  75.  * CImpIOleItemContainer::AddRef
  76.  * CImpIOleItemContainer::Release
  77.  */
  78.  
  79. STDMETHODIMP CImpIOleItemContainer::QueryInterface(REFIID riid
  80.     , PPVOID ppv)
  81.     {
  82.     return m_pUnkOuter->QueryInterface(riid, ppv);
  83.     }
  84.  
  85. STDMETHODIMP_(ULONG) CImpIOleItemContainer::AddRef(void)
  86.     {
  87.     ++m_cRef;
  88.     return m_pUnkOuter->AddRef();
  89.     }
  90.  
  91. STDMETHODIMP_(ULONG) CImpIOleItemContainer::Release(void)
  92.     {
  93.     --m_cRef;
  94.     return m_pUnkOuter->Release();
  95.     }
  96.  
  97.  
  98.  
  99. /*
  100.  * CImpIOleItemContainer::ParseDisplayName
  101.  *
  102.  * Purpose:
  103.  *  Parse the given name into a moniker as far as we know how
  104.  *  to parse.
  105.  *
  106.  * Parameters:
  107.  *  pbc             LPBC to the binding context
  108.  *  pszName         LPOLESTR to the name to parse.
  109.  *  pchEaten        ULONG * into which to store how many
  110.  *                  characters we scanned in the display name.
  111.  *  ppmk            LPMONIKER * in which to return the moniker.
  112.  *
  113.  * Return Value:
  114.  *  HRESULT         NOERROR or a general error value.
  115.  */
  116.  
  117. STDMETHODIMP CImpIOleItemContainer::ParseDisplayName(LPBC pbc
  118.     , LPOLESTR pszName, ULONG *pchEaten, LPMONIKER *ppmk)
  119.     {
  120.     OLECHAR     ch;
  121.     ULONG       chEaten=0;
  122.     TCHAR       szName[256];
  123.     TCHAR       szComp[15];
  124.     LPTSTR      psz;
  125.     UINT        cch;
  126.  
  127.     *ppmk=NULL;
  128.     *pchEaten=0;
  129.  
  130.     /*
  131.      * All we have to look for is the string between the !
  132.      * delimeters (or a null terminator).  pszName should be pointing
  133.      * to a !, so skip it and scan the string for a ! or 0,
  134.      * then pass the result to CreateItemMoniker.
  135.      */
  136.  
  137.     psz=szName;
  138.  
  139.     ch=*pszName++;
  140.     chEaten++;
  141.  
  142.     if ((OLECHAR)'!'!=ch)
  143.         return ResultFromScode(MK_E_SYNTAX);
  144.  
  145.     ch=*pszName++;
  146.  
  147.     while ((OLECHAR)0!=ch && (OLECHAR)'!' !=ch)
  148.         {
  149.         *psz++=(TCHAR)ch;
  150.         chEaten++;
  151.         ch=*pszName++;
  152.         }
  153.  
  154.     *psz=(TCHAR)0;
  155.  
  156.     /*
  157.      * Syntax check.  If we're the File object, check for "Object n"
  158.      * at the beginning of the string.  Otherwise check for
  159.      * "Sub-Object n".
  160.      */
  161.     lstrcpy(szComp, m_fFileObj ? TEXT("Object ")
  162.         : TEXT("Sub-Object "));
  163.  
  164.     //Does szName start with szComp?
  165.     cch=lstrlen(szComp);
  166.  
  167.     if (0!=_tcsncicmp(szName, szComp, cch))
  168.         {
  169.         *pchEaten=1;    //Parsed ! at least
  170.         return ResultFromScode(MK_E_SYNTAX);
  171.         }
  172.  
  173.     //Check for a number in szName
  174.     if ((TCHAR)'0' != szName[cch])
  175.         {
  176.         if (0==_ttoi(szName+cch))
  177.             {
  178.             *pchEaten=cch;  //Got past name
  179.             return ResultFromScode(MK_E_SYNTAX);
  180.             }
  181.         }
  182.  
  183.     *pchEaten=chEaten;
  184.    #ifdef WIN32ANSI
  185.     //Use the ANSI version here since szName is ANSI
  186.     return INOLE_CreateItemMoniker(TEXT("!"), szName, ppmk);
  187.    #else
  188.     return CreateItemMoniker(OLETEXT("!"), szName, ppmk);
  189.    #endif
  190.     }
  191.  
  192.  
  193.  
  194.  
  195. /*
  196.  * CImpIOleItemContainer::EnumObjects
  197.  *
  198.  * Purpose:
  199.  *  Creates and returns an IEnumUnknown object that allows the
  200.  *  caller to walk through the objects in this continer thing.
  201.  *
  202.  * Parameters:
  203.  *  dwFlags         DWORD specifying what kind of objects to
  204.  *                  enumerate.
  205.  *  ppEnum          LPENUMUNKNOWN * into which to return the
  206.  *                  enumerator
  207.  *
  208.  * Return Value:
  209.  *  HRESULT         NOERROR or a general error value.
  210.  */
  211.  
  212. STDMETHODIMP CImpIOleItemContainer::EnumObjects(DWORD dwFlags
  213.     , LPENUMUNKNOWN *ppEnum)
  214.     {
  215.     *ppEnum=NULL;
  216.  
  217.     /*
  218.      * We can leave this unimplemented in this sample since
  219.      * we know no one will ever call it.  A real container
  220.      * should be able to enumerate its contents.
  221.      */
  222.     return ResultFromScode(E_NOTIMPL);
  223.     }
  224.  
  225.  
  226.  
  227.  
  228. /*
  229.  * CImpIOleItemContainer::LockContainer
  230.  *
  231.  * Purpose:
  232.  *  Establishes a lock on the container to prevent it from shutting
  233.  *  down outside of user control.  This is used to control the
  234.  *  lifetime of the container when it's used to update a link to an
  235.  *  embedded object within it.  If we're unlock and the user has not
  236.  *  taken control, we close.
  237.  *
  238.  * Parameters:
  239.  *  fLock           BOOL indicating a lock or unlock.
  240.  *
  241.  * Return Value:
  242.  *  HRESULT         NOERROR or a general error value.
  243.  */
  244.  
  245. STDMETHODIMP CImpIOleItemContainer::LockContainer(BOOL fLock)
  246.     {
  247.     /*
  248.      * For the purposes of this server, we need only AddRef
  249.      * and release ourselves depending on fLock.
  250.      */
  251.  
  252.     if (fLock)
  253.         AddRef();
  254.     else
  255.         Release();
  256.  
  257.     return NOERROR;
  258.     }
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265. /*
  266.  * CImpIOleItemContainer::GetObject
  267.  *
  268.  * Purpose:
  269.  *  Returns the requested interface pointer on an object in this
  270.  *  container.
  271.  *
  272.  * Parameters:
  273.  *  pszItem         LPOLESTR to the item we must locate.
  274.  *  dwSpeed         DWORD identifying how long the caller is willing
  275.  *                  to wait.
  276.  *  pcb             LPBINDCTX providing the binding context.
  277.  *  riid            REFIID of the interface requested.
  278.  *  ppv             PPVOID into which to return the object.
  279.  *
  280.  * Return Value:
  281.  *  HRESULT         NOERROR or a general error value.
  282.  */
  283.  
  284. STDMETHODIMP CImpIOleItemContainer::GetObject(LPOLESTR pszItem
  285.     , DWORD dwSpeed, LPBINDCTX pbc, REFIID riid, PPVOID ppv)
  286.     {
  287.     HRESULT         hr;
  288.     IStorage       *pIStorage;
  289.     PCContainerItem pCI;
  290.     PCSimpleItem    pSI;
  291.     BOOL            fSuccess;
  292.     IUnknown       *pUnk;
  293.  
  294.     *ppv=NULL;
  295.  
  296.     /*
  297.      * The item name given to either CFileObj or CContainerItem
  298.      * instantiations of this interface will be the name of
  299.      * a storage below the current object's storage.  So we
  300.      * use m_fFileObj to know which storage to look under
  301.      * (names are either "Object n" or "Sub-Object n").
  302.      *
  303.      * If that storage exists, the object exists.  If it is
  304.      * already running, we can check in the Running Object Table
  305.      * and just return that object.  This is the only case that
  306.      * works when the bind speed in the bind context is
  307.      * immediate.
  308.      *
  309.      * Otherwise we open the storage and hand it to a new
  310.      * instance of the right type of object (CContainerItem or
  311.      * CSimpleItem), and register the new objects as running.
  312.      */
  313.  
  314.     //Get the object if running
  315.     hr=GetRunning(pszItem, pbc, riid, ppv, FALSE);
  316.  
  317.     if (BINDSPEED_IMMEDIATE==dwSpeed && NOERROR!=hr)
  318.         return ResultFromScode(MK_E_EXCEEDEDDEADLINE);
  319.  
  320.     //If object was running, we're done!
  321.     if (NOERROR==hr)
  322.         return NOERROR;
  323.  
  324.     //Otherwise we need to get the storage.
  325.     hr=GetObjectStorage(pszItem, pbc, IID_IStorage
  326.         , (void **)&pIStorage);
  327.  
  328.     if (FAILED(hr))
  329.         return hr;
  330.  
  331.     /*
  332.      * Storage exists, so create an object and give that storage
  333.      * to it.  The new object will also register itself in the
  334.      * Running Object Table.
  335.      */
  336.     fSuccess=FALSE;
  337.  
  338.     if (m_fFileObj)
  339.         {
  340.         pCI=new CContainerItem(m_pObjFile, m_pObjFile->m_pfnDestroy);
  341.  
  342.         pUnk=pCI;
  343.  
  344.         if (NULL!=pCI)
  345.             {
  346.             pUnk->AddRef();
  347.             fSuccess=pCI->Init(m_pObjFile->m_pmk, pbc, pszItem
  348.                 , pIStorage);
  349.             }
  350.         }
  351.     else
  352.         {
  353.         pSI=new CSimpleItem(m_pObjCont, m_pObjCont->m_pfnDestroy);
  354.  
  355.         pUnk=pSI;
  356.  
  357.         if (NULL!=pSI)
  358.             {
  359.             pUnk->AddRef();
  360.             fSuccess=pSI->Init(m_pObjCont->m_pmk, pbc, pszItem
  361.                 , pIStorage);
  362.             }
  363.         }
  364.  
  365.     if (!fSuccess)
  366.         {
  367.         if (NULL!=pUnk)
  368.             pUnk->Release();
  369.  
  370.         return ResultFromScode(E_OUTOFMEMORY);
  371.         }
  372.  
  373.     g_cObj++;
  374.  
  375.     //If QueryInterface fails, this Release destroys the object
  376.     hr=pUnk->QueryInterface(riid, ppv);
  377.     pUnk->Release();
  378.  
  379.     if (FAILED(hr))
  380.         return hr;
  381.  
  382.     return NOERROR;
  383.     }
  384.  
  385.  
  386.  
  387.  
  388.  
  389.  
  390. /*
  391.  * CImpIOleItemContainer::GetObjectStorage
  392.  *
  393.  * Purpose:
  394.  *  Similar to GetObject in that we have to locate the object
  395.  *  described by a given name, but instead of returning any old
  396.  *  interface we return a storage element.
  397.  *
  398.  * Parameters:
  399.  *  pszItem         LPOLESTR to the item we must locate.
  400.  *  pcb             LPBINDCTX providing the binding context.
  401.  *  riid            REFIID of the interface requested.  Usually
  402.  *                  IStorage or IStream.
  403.  *  ppv             PPVOID into which to return the object.
  404.  *
  405.  * Return Value:
  406.  *  HRESULT         NOERROR or a general error value.
  407.  */
  408.  
  409. STDMETHODIMP CImpIOleItemContainer::GetObjectStorage(LPOLESTR pszItem
  410.     , LPBINDCTX pbc, REFIID riid, PPVOID ppv)
  411.     {
  412.     IStorage       *pIStorageObj;
  413.     IStorage       *pIStorageNew;
  414.     HRESULT         hr;
  415.  
  416.     if (IID_IStorage!=riid)
  417.         return ResultFromScode(MK_E_NOSTORAGE);
  418.  
  419.     pIStorageObj=m_fFileObj ? m_pObjFile->m_pIStorage
  420.         : m_pObjCont->m_pIStorage;
  421.  
  422.     //Check storage existence
  423.     hr=pIStorageObj->OpenStorage(pszItem
  424.         , NULL, STGM_TRANSACTED | STGM_READ | STGM_SHARE_EXCLUSIVE
  425.         , NULL, 0, &pIStorageNew);
  426.  
  427.     if (FAILED(hr))
  428.         {
  429.         IUnknown   *pUnk;
  430.  
  431.         /*
  432.          * Because you must open a substorage with exclusive access,
  433.          * we'll run into a problem where an object created for use
  434.          * with parsing will still be running when another object of
  435.          * the same type is used for binding.  The reason is that the
  436.          * bind context holds these thing for optimization purposes.
  437.          * But that does mean that OpenStorage above might fail
  438.          * with STG_E_ACCESSDENIED.  In this case, we'll try to get
  439.          * a previously opened storage from the bind context
  440.          * (see below).
  441.          */
  442.         if (STG_E_ACCESSDENIED!=GetScode(hr))
  443.             return hr;
  444.  
  445.         if (FAILED(pbc->GetObjectParam(SZOPENSTORAGE, &pUnk)))
  446.             return ResultFromScode(STG_E_ACCESSDENIED);
  447.  
  448.         //This does the necessary AddRef on the IStorage pointer
  449.         hr=pUnk->QueryInterface(IID_IStorage
  450.             , (void **)&pIStorageNew);
  451.         pUnk->Release();
  452.  
  453.         //This sets the out-parameter to NULL on failure
  454.         *ppv=pIStorageNew;
  455.         return hr;
  456.         }
  457.  
  458.     *ppv=pIStorageNew;
  459.     pbc->RegisterObjectParam(SZOPENSTORAGE, pIStorageNew);
  460.     return NOERROR;
  461.     }
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468. /*
  469.  * CImpIOleItemContainer::IsRunning
  470.  *
  471.  * Purpose:
  472.  *  Answers if the object under the given name is currently running.
  473.  *
  474.  * Parameters:
  475.  *  pszItem         LPOLESTR of the item to check
  476.  *
  477.  * Return Value:
  478.  *  HRESULT         NOERROR if the object is running, S_FALSE
  479.  *                  otherwise.  Possibly MK_E_NOOBJECT if the name
  480.  *                  is bogus.
  481.  */
  482.  
  483. STDMETHODIMP CImpIOleItemContainer::IsRunning(LPOLESTR pszItem)
  484.     {
  485.     return GetRunning(pszItem, NULL, IID_NULL, NULL, TRUE);
  486.     }
  487.  
  488.  
  489.  
  490.  
  491.  
  492. /*
  493.  * (Internal)
  494.  * CImpIOleItemContainer::GetRunning
  495.  *
  496.  * Purpose:
  497.  *  Grabs a running object from the running object table or
  498.  *  just checks existence.
  499.  *
  500.  * Parameters:
  501.  *  pszItem         LPOLESTR of the item to check
  502.  *  pbc             IBindCtx * to use, can be NULL.
  503.  *  riid            REFIID to return if fCheck is FALSE.
  504.  *  ppv             void ** in which to return a pointer.
  505.  *  fCheck          BOOL indicating to check (TRUE) or
  506.  *                  retrieve (FALSE).
  507.  *
  508.  * Return Value:
  509.  *  HRESULT         NOERROR if the object is running, S_FALSE
  510.  *                  or an error otherwise.
  511.  */
  512.  
  513. HRESULT CImpIOleItemContainer::GetRunning(LPOLESTR pszItem
  514.     , IBindCtx *pbc, REFIID riid, void **ppv, BOOL fCheck)
  515.     {
  516.     OLECHAR              szDelim[]=OLETEXT("!");
  517.     HRESULT              hr=ResultFromScode(S_FALSE);
  518.     IMoniker            *pmkBase;
  519.     IMoniker            *pmkItem;
  520.     IMoniker            *pmkComp;
  521.     IRunningObjectTable *pROT;
  522.  
  523.     pmkBase=m_fFileObj ? m_pObjFile->m_pmk : m_pObjCont->m_pmk;
  524.  
  525.     if (FAILED(CreateItemMoniker(szDelim, pszItem, &pmkItem)))
  526.         return hr;
  527.  
  528.     //Create a composite with this item
  529.     hr=pmkBase->ComposeWith(pmkItem, FALSE, &pmkComp);
  530.  
  531.     if (FAILED(hr))
  532.         {
  533.         pmkItem->Release();
  534.         return hr;
  535.         }
  536.  
  537.  
  538.     if (NULL!=pbc)
  539.         hr=pbc->GetRunningObjectTable(&pROT);
  540.     else
  541.         hr=GetRunningObjectTable(0, &pROT);
  542.  
  543.     if (SUCCEEDED(hr))
  544.         {
  545.         hr=pROT->IsRunning(pmkComp);
  546.  
  547.         //If running, grab the object if necessary
  548.         if (!fCheck && NOERROR==hr)
  549.             {
  550.             IUnknown *pUnk;
  551.  
  552.             if(SUCCEEDED(pROT->GetObject(pmkComp, &pUnk)))
  553.                 {
  554.                 hr=pUnk->QueryInterface(riid, ppv);
  555.                 pUnk->Release();
  556.                 }
  557.             }
  558.  
  559.         pROT->Release();
  560.         }
  561.  
  562.     pmkComp->Release();
  563.     pmkItem->Release();
  564.     return hr;
  565.     }
  566.