|
|
////
//
// 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; i<newSize; ++i) newSlots[i].Construct();
// make the new array the new table and fix the total size
m_slots = newSlots; m_slotTableSize = newSize; }
// return a pointer to the slot and bump the totalSlots count
return &m_slots[m_totalSlots++]; }
////
//
// Free all of the slots
//
void CExpandoObject::FreeAllSlots() { UINT i; UINT initedSlotCount; CExpandoObjectSlot* slots;
// first clear the hash table
ClearHashTable();
// detach the slots
slots = m_slots; initedSlotCount = m_totalSlots;
// clear the object info
m_totalSlots = 0; m_slotTableSize = 0; m_slots = NULL;
// only need to destruct those slots in use
for (i=0; i<initedSlotCount; ++i) slots[i].Destruct();
// free the storage
if (slots) GTR_FREE(slots); }
////
//
// IDispatch Methods
//
////
HRESULT CExpandoObject::GetTypeInfoCount(UINT *pctinfo) { *pctinfo = 0; return NOERROR; }
HRESULT CExpandoObject::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo) { *pptinfo = NULL; return E_NOTIMPL; }
HRESULT CExpandoObject::GetIDsOfNames( REFIID riid, LPOLESTR *prgpsz, UINT cpsz, LCID lcid, DISPID *prgdispid ) { HRESULT hr;
if (IID_NULL != riid) return DISP_E_UNKNOWNINTERFACE;
// First see if the outer object knows about the name
if (m_pdisp) { hr = m_pdisp->GetIDsOfNames( 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; }
|