home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / activexcontrol / basectl / framewrk / extobj.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-04  |  10.2 KB  |  501 lines

  1. ////
  2. //
  3. // CExpandoObject
  4. //
  5. // Notes:
  6. // 1) If the LCID passed to this object changes from call to call we are screwed. This is hard to
  7. // create an ASSERT for because it would require memoizing the LCID at some point.
  8. // 2) There is a maximum on the number of slots allowed (this is currently 2048)
  9. // 3) This is not a thread safe structure.
  10. // 4) I'm currently using malloc -- this is probably wrong for IE.
  11. //
  12.  
  13. // for ASSERT and FAIL
  14. //
  15.  
  16. #include "IPServer.H"
  17. #include "LocalSrv.H"
  18. #include "Globals.H"
  19. #include "extobj.h" 
  20. #include "Util.H"
  21. #define GTR_MALLOC(size)  CoTaskMemAlloc(size)
  22. #define GTR_FREE(pv) CoTaskMemFree(pv)
  23.  
  24. SZTHISFILE
  25. ////
  26. //
  27. // Private Utility Functions
  28. //
  29. ////
  30.  
  31. ////
  32. //
  33. // Get the ID of a Name
  34. //
  35.  
  36. HRESULT CExpandoObject::GetIDOfName(LPOLESTR name, LCID lcid, BOOL caseSensitive, DISPID* id)
  37. {
  38.     HRESULT hr = NOERROR;
  39.     ULONG hash = LHashValOfName(lcid, name);
  40.     UINT hashIndex = hash % kSlotHashTableSize;
  41.     CExpandoObjectSlot* slot;
  42.  
  43.     for (slot=GetHashTableHead(hashIndex); slot!=NULL; slot=slot->Next(m_slots))
  44.     {
  45.         if (slot->CompareName(name, hash, caseSensitive))
  46.         {
  47.             *id = slot->DispId();
  48.             goto Exit;
  49.         }
  50.     }
  51.  
  52.     // not found
  53.     hr = DISP_E_UNKNOWNNAME;
  54.     *id = DISPID_UNKNOWN;
  55.  
  56. Exit:
  57.     return hr;
  58. }
  59.  
  60. ////
  61. //
  62. // Add a new slot to the object
  63. //
  64.  
  65. HRESULT CExpandoObject::AddSlot(LPOLESTR name, LCID lcid, BOOL caseSensitive, VARIANT* initialValue, DISPID* id)
  66. {
  67.     HRESULT hr = NOERROR;
  68.     ULONG hash = LHashValOfName(lcid, name);
  69.     UINT hashIndex = hash % kSlotHashTableSize;
  70.     CExpandoObjectSlot* slot;
  71.     DISPID    dispId;
  72.  
  73.     // first check if the slot exists
  74.     for (slot=GetHashTableHead(hashIndex); slot!=NULL; slot=slot->Next(m_slots))
  75.     {
  76.         // bail if the name matches
  77.         if (slot->CompareName(name, hash, caseSensitive))
  78.         {
  79.             hr = E_INVALIDARG;
  80.             goto Exit;
  81.         }
  82.     }
  83.  
  84.     // allocate a slot
  85.     dispId = (DISPID) m_totalSlots;
  86.     slot = AllocSlot();
  87.     if (slot == NULL)
  88.     {
  89.         hr = E_OUTOFMEMORY;
  90.         goto Exit;
  91.     }
  92.  
  93.     // Initialize it
  94.     // BUGBUG robwell 8May96 If this fails and the initialValue is not VT_EMTPY or VT_NULL
  95.     // there in no cleanup code.
  96.     hr = slot->Init(name, lcid, dispId + m_dispIdBase, initialValue);
  97.     if (FAILED(hr))
  98.     {
  99.         // free the slot and dispId
  100.         m_totalSlots -= 1;
  101.         goto Exit;
  102.     }
  103.  
  104.     // intern the slot into the proper hash table
  105.     slot->Insert(m_slots, m_hashTable[hashIndex]);
  106.  
  107.     // set the DISPID return value
  108.     *id = slot->DispId();
  109.  
  110. Exit:
  111.     return hr;
  112. }
  113.  
  114. ////
  115. //
  116. // Slot allocation
  117. //
  118. // Because slots are never freed there is no free method
  119. //
  120.  
  121. CExpandoObjectSlot* CExpandoObject::AllocSlot()
  122. {
  123.     // limit on the number of slots
  124.     if (m_totalSlots >= kMaxTotalSlots)
  125.         return NULL;
  126.  
  127.     // do we need to realloc the array?
  128.     if (m_totalSlots == m_slotTableSize)
  129.     {
  130.         UINT i;
  131.         UINT newSize;
  132.         CExpandoObjectSlot* newSlots;
  133.  
  134.         // allocate twice as many slots unless first time around
  135.         if (m_slotTableSize == 0)
  136.             newSize = kInitialSlotTableSize;
  137.         else
  138.             newSize = m_slotTableSize * 2;
  139.  
  140.         // allocate the space for the slots
  141.         newSlots = (CExpandoObjectSlot*) GTR_MALLOC(sizeof(CExpandoObjectSlot)*newSize);
  142.         if (newSlots == NULL)
  143.             return NULL;
  144.  
  145.         // copy the old values if the old m_slots is not NULL
  146.         if (m_slots)
  147.         {
  148.             // copy the slots
  149.             memcpy(newSlots, m_slots, sizeof(CExpandoObjectSlot)*m_totalSlots);
  150.             // free the old values
  151.             GTR_FREE(m_slots);
  152.         }
  153.  
  154.         // construct all of the unused slots
  155.         for (i=m_totalSlots; i<newSize; ++i)
  156.             newSlots[i].Construct();
  157.  
  158.         // make the new array the new table and fix the total size
  159.         m_slots = newSlots;
  160.         m_slotTableSize = newSize;
  161.     }
  162.  
  163.     // return a pointer to the slot and bump the totalSlots count
  164.     return &m_slots[m_totalSlots++];
  165. }
  166.  
  167. ////
  168. //
  169. // Free all of the slots
  170. //
  171.  
  172. void CExpandoObject::FreeAllSlots()
  173. {
  174.     UINT i;
  175.     UINT initedSlotCount;
  176.     CExpandoObjectSlot* slots;
  177.  
  178.     // first clear the hash table
  179.     ClearHashTable();
  180.  
  181.     // detach the slots
  182.     slots = m_slots;
  183.     initedSlotCount = m_totalSlots;
  184.  
  185.     // clear the object info
  186.     m_totalSlots = 0;
  187.     m_slotTableSize = 0;
  188.     m_slots = NULL;
  189.  
  190.     // only need to destruct those slots in use
  191.     for (i=0; i<initedSlotCount; ++i)
  192.         slots[i].Destruct();
  193.  
  194.     // free the storage
  195.     if (slots)
  196.         GTR_FREE(slots);
  197. }
  198.  
  199.  
  200.  
  201. ////
  202. //
  203. // IDispatch Methods
  204. //
  205. ////
  206.  
  207. HRESULT CExpandoObject::GetTypeInfoCount(UINT *pctinfo)
  208. {
  209.     *pctinfo = 0;
  210.     return NOERROR;
  211. }
  212.  
  213. HRESULT CExpandoObject::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
  214. {
  215.     *pptinfo = NULL;
  216.     return E_NOTIMPL;
  217. }
  218.  
  219. HRESULT CExpandoObject::GetIDsOfNames(
  220.     REFIID riid,
  221.     LPOLESTR *prgpsz,
  222.     UINT cpsz,
  223.     LCID lcid,
  224.     DISPID *prgdispid
  225. )
  226. {
  227.     HRESULT hr;
  228.  
  229.     if (IID_NULL != riid)
  230.         return DISP_E_UNKNOWNINTERFACE;
  231.  
  232.     // First see if the outer object knows about the name
  233.     if (m_pdisp)
  234.     {
  235.         hr = m_pdisp->GetIDsOfNames(
  236.             riid,
  237.             prgpsz,
  238.             cpsz,
  239.             lcid,
  240.             prgdispid);
  241.  
  242.         // if so, just return
  243.         if (SUCCEEDED(hr))
  244.             return hr;
  245.     }
  246.  
  247.     // Otherwise look on our expanded properties
  248.  
  249.     if (cpsz == 0)
  250.         return NOERROR;
  251.  
  252.     // get the ids for the name
  253.     hr = GetIDOfName(prgpsz[0], lcid, FALSE, &prgdispid[0]);
  254.  
  255.     // clear the rest of the array
  256.     for (unsigned int i = 1; i < cpsz; i++)
  257.     {
  258.         if (SUCCEEDED(hr))
  259.             hr = DISP_E_UNKNOWNNAME;
  260.         prgdispid[i] = DISPID_UNKNOWN;
  261.     }
  262.  
  263.     return hr;
  264. }
  265.  
  266. HRESULT CExpandoObject::Invoke(
  267.     DISPID dispID,
  268.     REFIID riid,
  269.     LCID lcid,
  270.     WORD wFlags,
  271.     DISPPARAMS *pdispparams,
  272.     VARIANT *pvarRes,
  273.     EXCEPINFO *pexcepinfo,
  274.     UINT *puArgErr
  275. )
  276. {
  277.     if (IID_NULL != riid)
  278.         return DISP_E_UNKNOWNINTERFACE;
  279.  
  280.     HRESULT hr;
  281.  
  282.     // First try the outer object's invoke
  283.     if (m_pdisp)
  284.     {
  285.         hr = m_pdisp->Invoke(
  286.                 dispID,
  287.                 riid,
  288.                 lcid,
  289.                 wFlags,
  290.                 pdispparams,
  291.                 pvarRes,
  292.                 pexcepinfo,
  293.                 puArgErr
  294.         );
  295.  
  296.         // If that succeeded, we're done
  297.         if (SUCCEEDED(hr))
  298.             return hr;
  299.     }
  300.     
  301.     // Otherwise, try the expando object's invoke    
  302.     if (NULL != puArgErr)
  303.         *puArgErr = 0;
  304.  
  305.     if (wFlags & DISPATCH_PROPERTYGET)
  306.     {
  307.         if (NULL == pvarRes)
  308.             return NOERROR;
  309.  
  310.         if (NULL != pdispparams && 0 != pdispparams->cArgs)
  311.             return E_INVALIDARG;
  312.  
  313.         // clear the result slot
  314.         pvarRes->vt = VT_EMPTY;
  315.         return GetSlot(dispID, pvarRes);
  316.     }
  317.  
  318.     if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
  319.     {
  320.         if (NULL == pdispparams
  321.         || 1 != pdispparams->cArgs
  322.         || 1 != pdispparams->cNamedArgs
  323.         || DISPID_PROPERTYPUT != pdispparams->rgdispidNamedArgs[0]
  324.         )
  325.             return DISP_E_PARAMNOTOPTIONAL;
  326.  
  327.         return SetSlot(dispID, &pdispparams->rgvarg[0]);
  328.     }
  329.  
  330.     return DISP_E_MEMBERNOTFOUND;
  331. }
  332.  
  333. ////
  334. //
  335. // IDispatchEx methods
  336. //
  337. ////
  338.  
  339. // Get dispID for names, with options
  340. HRESULT STDMETHODCALLTYPE CExpandoObject::GetIDsOfNamesEx(
  341.     REFIID riid,
  342.     LPOLESTR *prgpsz,
  343.     UINT cpsz,
  344.     LCID lcid,
  345.     DISPID *prgid,
  346.     DWORD grfdex
  347. )
  348. {
  349.     HRESULT hr;
  350.     BOOL caseSensitive = ((grfdex & fdexCaseSensitive) != 0);
  351.  
  352.  
  353.     // First see if the outer object knows about the name
  354.     if (m_pdisp)
  355.     {
  356.         hr = m_pdisp->GetIDsOfNames(
  357.             riid,
  358.             prgpsz,
  359.             cpsz,
  360.             lcid,
  361.             prgid);
  362.  
  363.         // if so, just return
  364.         if (SUCCEEDED(hr))
  365.             return hr;
  366.     }
  367.  
  368.  
  369.     if (IID_NULL != riid)
  370.         return DISP_E_UNKNOWNINTERFACE;
  371.  
  372.     if (cpsz == 0)
  373.         return NOERROR;
  374.  
  375.     // check the array arguments
  376.     if (prgpsz == NULL || prgid == NULL)
  377.         return E_INVALIDARG;
  378.  
  379.     // get the id from the name
  380.     hr = GetIDOfName(prgpsz[0], lcid, caseSensitive, &prgid[0]);
  381.  
  382.     // create the slot?
  383.     if (hr == DISP_E_UNKNOWNNAME && (grfdex & fdexDontCreate) == 0)
  384.     {
  385.         VARIANT initialValue;
  386.  
  387.         if (grfdex & fdexInitNull)
  388.             initialValue.vt = VT_NULL;
  389.         else
  390.             initialValue.vt = VT_EMPTY;
  391.  
  392.         hr = AddSlot(prgpsz[0], lcid, caseSensitive, &initialValue, &prgid[0]);
  393.     }
  394.  
  395.     // clear the rest of the array
  396.     for (unsigned int i = 1; i < cpsz; i++)
  397.     {
  398.         hr = DISP_E_UNKNOWNNAME;
  399.         prgid[i] = DISPID_UNKNOWN;
  400.     }
  401.  
  402.     return hr;
  403. }
  404.  
  405. // Enumerate dispIDs and their associated "names".
  406. // Returns S_FALSE if the enumeration is done, NOERROR if it's not, an
  407. // error code if the call fails.
  408. HRESULT STDMETHODCALLTYPE CExpandoObject::GetNextDispID(
  409.     DISPID id,
  410.     DISPID *pid,
  411.     BSTR *pbstrName
  412. )
  413. {
  414.     HRESULT hr;
  415.     CExpandoObjectSlot* slot;
  416.  
  417.     // check the outgoing parameters
  418.     if (pid == NULL || pbstrName == NULL)
  419.         return E_INVALIDARG;
  420.  
  421.     // set to the default failure case
  422.     *pid = DISPID_UNKNOWN;
  423.     *pbstrName = NULL;
  424.  
  425.     // get the next slot
  426.     hr = Next(id, slot);
  427.     if (hr == NOERROR)
  428.     {
  429.         BSTR name;
  430.  
  431.         // allocate the result string
  432.         name = SysAllocString(slot->Name());
  433.         if (name == NULL)
  434.             return E_OUTOFMEMORY;
  435.  
  436.         // fill in the outgoing parameters
  437.         *pid = slot->DispId();
  438.         *pbstrName = name;
  439.     }
  440.  
  441.     return hr;
  442. }
  443.  
  444. // Copy all of the expando-object properties from obj
  445. CExpandoObject::CloneProperties(CExpandoObject& obj)
  446. {
  447.     // BUGBUG  PhilBo
  448.     // The initialization code below is copied from the default constructor.
  449.     // This should be factored out into a shared method.
  450.  
  451.     // Copy each of the properties from the original object
  452.     HRESULT hr = S_OK;
  453.     DISPID dispid = 0;
  454.     BSTR bstrName = NULL;
  455.     
  456.     while (obj.GetNextDispID(dispid, &dispid, &bstrName) == S_OK)
  457.     {
  458.         // Get the value of the property from the original object
  459.         VARIANT varResult;
  460.         DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; 
  461.         VariantInit(&varResult);
  462.  
  463.         hr = obj.Invoke( 
  464.                 dispid, 
  465.                 IID_NULL, 
  466.                 LOCALE_SYSTEM_DEFAULT, 
  467.                 DISPATCH_PROPERTYGET, 
  468.                 &dispparamsNoArgs, &varResult, NULL, NULL); 
  469.  
  470.         ASSERT(SUCCEEDED(hr), "");
  471.         if (FAILED(hr))
  472.             continue;
  473.  
  474.         // Set the property on the new object            
  475.         DISPID dispidNew = 0;
  476.         hr = GetIDsOfNamesEx(IID_NULL, &bstrName, 1, LOCALE_SYSTEM_DEFAULT,
  477.             &dispidNew, 0);
  478.  
  479.         ASSERT(SUCCEEDED(hr), "");
  480.         if (FAILED(hr))
  481.             continue;
  482.  
  483.         DISPPARAMS dispparams; 
  484.         dispparams.rgvarg[0] = varResult; 
  485.  
  486.         DISPID rgdispid[] = {DISPID_PROPERTYPUT};
  487.         dispparams.rgdispidNamedArgs = rgdispid; 
  488.         dispparams.cArgs = 1; 
  489.         dispparams.cNamedArgs = 1; 
  490.  
  491.         hr = Invoke( 
  492.             dispidNew, 
  493.             IID_NULL, 
  494.             LOCALE_SYSTEM_DEFAULT, 
  495.             DISPATCH_PROPERTYPUT, 
  496.             &dispparams, NULL, NULL, NULL); 
  497.     }
  498.  
  499.     return hr;
  500. }
  501.