//// // // CExpandoObject // // Notes: // 1) If the LCID passed to this object changes from call to call we are in trouble. This is hard to // create an ASSERT for because it would require memoizing the LCID at some point. // 2) There is a maximum on the number of slots allowed (this is currently 2048) // 3) This is not a thread safe structure. // 4) I'm currently using malloc -- this is probably wrong for IE. // // for ASSERT and FAIL // #include "IPServer.H" #include "LocalSrv.H" #include "Globals.H" #include "extobj.h" #include "Util.H" #define GTR_MALLOC(size) CoTaskMemAlloc(size) #define GTR_FREE(pv) CoTaskMemFree(pv) SZTHISFILE //// // // Private Utility Functions // //// //// // // Get the ID of a Name // HRESULT CExpandoObject::GetIDOfName(LPOLESTR name, LCID lcid, BOOL caseSensitive, DISPID* id) { HRESULT hr = NOERROR; ULONG hash = LHashValOfName(lcid, name); UINT hashIndex = hash % kSlotHashTableSize; CExpandoObjectSlot* slot; for (slot=GetHashTableHead(hashIndex); slot!=NULL; slot=slot->Next(m_slots)) { if (slot->CompareName(name, hash, caseSensitive)) { *id = slot->DispId(); goto Exit; } } // not found hr = DISP_E_UNKNOWNNAME; *id = DISPID_UNKNOWN; Exit: return hr; } //// // // Add a new slot to the object // HRESULT CExpandoObject::AddSlot(LPOLESTR name, LCID lcid, BOOL caseSensitive, VARIANT* initialValue, DISPID* id) { HRESULT hr = NOERROR; ULONG hash = LHashValOfName(lcid, name); UINT hashIndex = hash % kSlotHashTableSize; CExpandoObjectSlot* slot; DISPID dispId; // first check if the slot exists for (slot=GetHashTableHead(hashIndex); slot!=NULL; slot=slot->Next(m_slots)) { // bail if the name matches if (slot->CompareName(name, hash, caseSensitive)) { hr = E_INVALIDARG; goto Exit; } } // allocate a slot dispId = (DISPID) m_totalSlots; slot = AllocSlot(); if (slot == NULL) { hr = E_OUTOFMEMORY; goto Exit; } // Initialize it // BUGBUG robwell 8May96 If this fails and the initialValue is not VT_EMTPY or VT_NULL // there in no cleanup code. hr = slot->Init(name, lcid, dispId + m_dispIdBase, initialValue); if (FAILED(hr)) { // free the slot and dispId m_totalSlots -= 1; goto Exit; } // intern the slot into the proper hash table slot->Insert(m_slots, m_hashTable[hashIndex]); // set the DISPID return value *id = slot->DispId(); Exit: return hr; } //// // // Slot allocation // // Because slots are never freed there is no free method // CExpandoObjectSlot* CExpandoObject::AllocSlot() { // limit on the number of slots if (m_totalSlots >= kMaxTotalSlots) return NULL; // do we need to realloc the array? if (m_totalSlots == m_slotTableSize) { UINT i; UINT newSize; CExpandoObjectSlot* newSlots; // allocate twice as many slots unless first time around if (m_slotTableSize == 0) newSize = kInitialSlotTableSize; else newSize = m_slotTableSize * 2; // allocate the space for the slots newSlots = (CExpandoObjectSlot*) GTR_MALLOC(sizeof(CExpandoObjectSlot)*newSize); if (newSlots == NULL) return NULL; // copy the old values if the old m_slots is not NULL if (m_slots) { // copy the slots memcpy(newSlots, m_slots, sizeof(CExpandoObjectSlot)*m_totalSlots); // free the old values GTR_FREE(m_slots); } // construct all of the unused slots for (i=m_totalSlots; iGetIDsOfNames( riid, prgpsz, cpsz, lcid, prgdispid); // if so, just return if (SUCCEEDED(hr)) return hr; } // Otherwise look on our expanded properties if (cpsz == 0) return NOERROR; // get the ids for the name hr = GetIDOfName(prgpsz[0], lcid, FALSE, &prgdispid[0]); // clear the rest of the array for (unsigned int i = 1; i < cpsz; i++) { if (SUCCEEDED(hr)) hr = DISP_E_UNKNOWNNAME; prgdispid[i] = DISPID_UNKNOWN; } return hr; } HRESULT CExpandoObject::Invoke( DISPID dispID, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarRes, EXCEPINFO *pexcepinfo, UINT *puArgErr ) { if (IID_NULL != riid) return DISP_E_UNKNOWNINTERFACE; HRESULT hr; // First try the outer object's invoke if (m_pdisp) { hr = m_pdisp->Invoke( dispID, riid, lcid, wFlags, pdispparams, pvarRes, pexcepinfo, puArgErr ); // If that succeeded, we're done if (SUCCEEDED(hr)) return hr; } // Otherwise, try the expando object's invoke if (NULL != puArgErr) *puArgErr = 0; if (wFlags & DISPATCH_PROPERTYGET) { if (NULL == pvarRes) return NOERROR; if (NULL != pdispparams && 0 != pdispparams->cArgs) return E_INVALIDARG; // clear the result slot pvarRes->vt = VT_EMPTY; return GetSlot(dispID, pvarRes); } if (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) { if (NULL == pdispparams || 1 != pdispparams->cArgs || 1 != pdispparams->cNamedArgs || DISPID_PROPERTYPUT != pdispparams->rgdispidNamedArgs[0] ) return DISP_E_PARAMNOTOPTIONAL; return SetSlot(dispID, &pdispparams->rgvarg[0]); } return DISP_E_MEMBERNOTFOUND; } //// // // IDispatchEx methods // //// // Get dispID for names, with options HRESULT STDMETHODCALLTYPE CExpandoObject::GetIDsOfNamesEx( REFIID riid, LPOLESTR *prgpsz, UINT cpsz, LCID lcid, DISPID *prgid, DWORD grfdex ) { HRESULT hr; BOOL caseSensitive = ((grfdex & fdexCaseSensitive) != 0); // First see if the outer object knows about the name if (m_pdisp) { hr = m_pdisp->GetIDsOfNames( riid, prgpsz, cpsz, lcid, prgid); // if so, just return if (SUCCEEDED(hr)) return hr; } if (IID_NULL != riid) return DISP_E_UNKNOWNINTERFACE; if (cpsz == 0) return NOERROR; // check the array arguments if (prgpsz == NULL || prgid == NULL) return E_INVALIDARG; // get the id from the name hr = GetIDOfName(prgpsz[0], lcid, caseSensitive, &prgid[0]); // create the slot? if (hr == DISP_E_UNKNOWNNAME && (grfdex & fdexDontCreate) == 0) { VARIANT initialValue; if (grfdex & fdexInitNull) initialValue.vt = VT_NULL; else initialValue.vt = VT_EMPTY; hr = AddSlot(prgpsz[0], lcid, caseSensitive, &initialValue, &prgid[0]); } // clear the rest of the array for (unsigned int i = 1; i < cpsz; i++) { hr = DISP_E_UNKNOWNNAME; prgid[i] = DISPID_UNKNOWN; } return hr; } // Enumerate dispIDs and their associated "names". // Returns S_FALSE if the enumeration is done, NOERROR if it's not, an // error code if the call fails. HRESULT STDMETHODCALLTYPE CExpandoObject::GetNextDispID( DISPID id, DISPID *pid, BSTR *pbstrName ) { HRESULT hr; CExpandoObjectSlot* slot; // check the outgoing parameters if (pid == NULL || pbstrName == NULL) return E_INVALIDARG; // set to the default failure case *pid = DISPID_UNKNOWN; *pbstrName = NULL; // get the next slot hr = Next(id, slot); if (hr == NOERROR) { BSTR name; // allocate the result string name = SysAllocString(slot->Name()); if (name == NULL) return E_OUTOFMEMORY; // fill in the outgoing parameters *pid = slot->DispId(); *pbstrName = name; } return hr; } // Copy all of the expando-object properties from obj HRESULT CExpandoObject::CloneProperties(CExpandoObject& obj) { // BUGBUG PhilBo // The initialization code below is copied from the default constructor. // This should be factored out into a shared method. // Copy each of the properties from the original object HRESULT hr = S_OK; DISPID dispid = 0; BSTR bstrName = NULL; while (obj.GetNextDispID(dispid, &dispid, &bstrName) == S_OK) { // Get the value of the property from the original object VARIANT varResult; DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; VariantInit(&varResult); hr = obj.Invoke( dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &varResult, NULL, NULL); ASSERT(SUCCEEDED(hr), ""); if (FAILED(hr)) continue; // Set the property on the new object DISPID dispidNew = 0; hr = GetIDsOfNamesEx(IID_NULL, &bstrName, 1, LOCALE_SYSTEM_DEFAULT, &dispidNew, 0); ASSERT(SUCCEEDED(hr), ""); if (FAILED(hr)) continue; DISPPARAMS dispparams; dispparams.rgvarg = &varResult; DISPID rgdispid[] = {DISPID_PROPERTYPUT}; dispparams.rgdispidNamedArgs = rgdispid; dispparams.cArgs = 1; dispparams.cNamedArgs = 1; hr = Invoke( dispidNew, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); } return hr; }