WindowsXP-SP1/shell/iecontrols/framewrk/extobj.cpp
2020-09-30 16:53:49 +02:00

502 lines
10 KiB
C++

////
//
// 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 = {0};
dispparams.rgvarg[0] = 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;
}