EXTOBJ.H

#ifndef __EXTOBJ_H 
#define __EXTOBJ_H
////
//
// ExpandoObject header file
//
//
//
#include "IPServer.H"

////
//
// IDispatchEx
//
////

////
//
// the GUID
//

// {A0AAC450-A77B-11cf-91D0-00AA00C14A7C}
DEFINE_GUID(IID_IDispatchEx, 0xa0aac450, 0xa77b, 0x11cf, 0x91, 0xd0, 0x0, 0xaa, 0x0, 0xc1, 0x4a, 0x7c);

////
//
// IDispatchEx flags:
//

enum
{
fdexNil = 0x00,// empty
fdexDontCreate = 0x01,// don't create slot if non-existant otherwise do
fdexInitNull = 0x02,// init a new slot to VT_NULL as opposed to VT_EMPTY
fdexCaseSensitive = 0x04,// match names as case sensitive
};

////
//
// This is the interface for extensible IDispatch objects.
//

class IDispatchEx : public IDispatch
{
public:
// Get dispID for names, with options
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNamesEx(
REFIID riid,
LPOLESTR *prgpsz,
UINT cpsz,
LCID lcid,
DISPID *prgid,
DWORD grfdex
) = 0;

// 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.
virtual HRESULT STDMETHODCALLTYPE GetNextDispID(
DISPID id,
DISPID *pid,
BSTR *pbstrName
) = 0;
};

////
//
// Globals and definitions
//
////

#define NUM_EXPANDO_DISPIDS250
#defineNUM_CORE_DISPIDS250
#define NUM_RESERVED_EXTENDER_DISPIDS (NUM_CORE_DISPIDS + NUM_EXPANDO_DISPIDS)
#define EXTENDER_DISPID_BASE ((ULONG)(0x80010000))
#define IS_EXTENDER_DISPID(x) ( ( (ULONG)(x) & 0xFFFF0000 ) == EXTENDER_DISPID_BASE )

////
//
// Slot: the state of a value slot
//

inline WCHAR ToUpper(WCHAR ch)
{
if (ch>='a' && ch <= 'z')
return ch - 'a' + 'A';
else
return ch;

}

class CExpandoObjectSlot
{
public:
////
//
// Constructor/Destructor
//

// because these monsters are malloc'ed, we need a manual constructor and destructor methods
void Construct()
{
m_name = NULL;
m_next = -1;
VariantInit(&m_value);
// set hash and dispId to dummy values
m_hash = 0;
m_dispId = DISPID_UNKNOWN;
}

void Destruct()
{
if (m_name)
SysFreeString(m_name);
VariantClear(&m_value);
}

private:
// the constructors and destructors are private because they should never be called ...
// we could use in-place construction if we wanted to be clever ...
CExpandoObjectSlot()
{
}

~CExpandoObjectSlot()
{
}

public:
////
//
// Init the slot
//

HRESULT Init(LPOLESTR name, LCID lcid, DISPID dispId, VARIANT* value)
{
// allocate the string
m_name = SysAllocString(name);
if (m_name == NULL)
return E_OUTOFMEMORY;

// compute the hash: uses the standard OLE string hashing function
// note that this function is case insensitive
m_hash = LHashValOfName(lcid, name);

// set the dispId
m_dispId = dispId;

// Copy the variant value
return VariantCopy(&m_value, value);
}

////
//
// Name information
//

// get the name
BSTR Name()
{ return m_name; }

// compare two names
BOOL CompareName(LPOLESTR name, ULONG hash, BOOL caseSensitive)
{
// hash should be the same, length should be the same, and strings should compare
// BUGBUG robwell 8May96 These functions are probably verboten.
if (hash != m_hash)
return FALSE;

if (!name)
return !m_name;

WCHAR *c1 = name;
WCHAR *c2 = m_name;

// Travel down both strings until we reach a mismatched character
// or the end of one (or both) of the strings

if (caseSensitive)
while (*c1 && *c2 && *c1++==*c2++);
else
while (*c1 && *c2 && ToUpper(*c1++)==ToUpper(*c2++));

// The strings match if we reached the end of both without a mismatch
return !*c1 && !*c2;
}

////
//
// DispId information
//

// get the dispatch id
DISPID DispId()
{ return m_dispId; }

////
//
// Get and set the property values
//

HRESULT Get(VARIANT* result)
{ return VariantCopy(result, &m_value); }

HRESULT Set(VARIANT* value)
{ return VariantCopy(&m_value, value); }

////
//
// List management
//

CExpandoObjectSlot* Next(CExpandoObjectSlot* base)
{ return m_next == -1? NULL: &base[m_next]; }

CExpandoObjectSlot* Insert(CExpandoObjectSlot* base, LONG& prev)
{
m_next = prev;
prev = this - base;
return this;
}

private:
// the DispId
DISPIDm_dispId;
// the name
LPOLESTRm_name;
// the name hash
ULONGm_hash;
// the property value
VARIANTm_value;
// the hash bucket link (index based)
LONGm_next;
};

// NB: CExpandoObject implements a crippled version of aggegation.
// It delegates all IUnknown calls to its controlling IUnknown, and has no
// private IUnknown interface.
// If you want the CExpandoObject to go away, simply call delete on it.
class CExpandoObject: public IDispatchEx
{
public:

////
//
// Constructor/Destructor
//

CExpandoObject(IUnknown *punkOuter, IDispatch *pdisp, ULONG dispIdBase = EXTENDER_DISPID_BASE + NUM_CORE_DISPIDS)
{
// remember our controlling outer
m_punkOuter = punkOuter;

// remember the IDispatch to try first for IDispatch functionality
m_pdisp = pdisp;

// clear the name hash table
ClearHashTable();
// set the total slots and the table of slots to 0 and empty respectively)
m_totalSlots = 0;
m_slotTableSize = 0;
m_slots = NULL;
m_dispIdBase = dispIdBase;
}

STDMETHODIMP_(ULONG) AddRef()
{
return m_punkOuter->AddRef();
}

STDMETHODIMP_(ULONG)Release()
{
return m_punkOuter->Release();
}

STDMETHODIMP QueryInterface(REFIID riid, void **ppvObjOut)
{
return m_punkOuter->QueryInterface(riid, ppvObjOut);
}

virtual ~CExpandoObject(void)
{
FreeAllSlots();
}


// Copy all of the properties from obj
CloneProperties(CExpandoObject& obj);

////
//
//
// Utility functions
//

// free all slots
void FreeAllSlots();

// IDispatch methods
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo);
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
UINT itinfo,
LCID lcid,
ITypeInfo **pptinfo
);
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(
REFIID riid,
LPOLESTR *prgpsz,
UINT cpsz,
LCID lcid,
DISPID *prgdispID
);
virtual HRESULT STDMETHODCALLTYPE Invoke(
DISPID dispID,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pdispparams,
VARIANT *pvarRes,
EXCEPINFO *pexcepinfo,
UINT *puArgErr
);

// IDispatchEx methods

// Get dispID for names, with options
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNamesEx(
REFIID riid,
LPOLESTR *prgpsz,
UINT cpsz,
LCID lcid,
DISPID *prgid,
DWORD grfdex
);

// 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.
virtual HRESULT STDMETHODCALLTYPE GetNextDispID(
DISPID id,
DISPID *pid,
BSTR *pbstrName
);

private:
////
//
// Implementation constants
//

enum
{
kSlotHashTableSize = 10,
kInitialSlotTableSize = 4,
kMaxTotalSlots = NUM_EXPANDO_DISPIDS
};

////
//
// Utility functions
//

//
CExpandoObjectSlot* GetHashTableHead(UINT hashIndex)
{
LONG index;

return (index = m_hashTable[hashIndex]) == -1? NULL: &m_slots[index];
}

// get the ID of from a slot name
HRESULT GetIDOfName(LPOLESTR name, LCID lcid, BOOL caseSensitive, DISPID* id);
// add a slot to the object
HRESULT AddSlot(LPOLESTR name, LCID lcid, BOOL caseSensitive, VARIANT* initialValue, DISPID* id);
// allocate a slot from the slot table
CExpandoObjectSlot* AllocSlot();
// clear the hash table
void ClearHashTable()
{
UINT i;

for (i=0; i<kSlotHashTableSize; ++i)
m_hashTable[i] = -1;
}

////
//
// Slot operations
//
// DISPIDS start at kInitialDispId so we need to offset them by that amount
// in this code.
//

HRESULT GetSlot(DISPID id, VARIANT* result)
{
if ((ULONG) id < m_dispIdBase || (ULONG) id >= (m_totalSlots+m_dispIdBase))
return DISP_E_MEMBERNOTFOUND;

return m_slots[id-m_dispIdBase].Get(result);
}

HRESULT SetSlot(DISPID id, VARIANT* result)
{
if ((ULONG) id < m_dispIdBase || (ULONG) id >= (m_totalSlots+m_dispIdBase))
return DISP_E_MEMBERNOTFOUND;

return m_slots[id-m_dispIdBase].Set(result);
}

////
//
// Iteration operations
//

UINTNumDispIds()
{ return m_totalSlots; }

DISPIDFirst()
{ return m_dispIdBase; }

DISPIDLast()
{ return m_totalSlots + m_dispIdBase - 1; }

BOOLValidDispId(DISPID id)
{ return id >= First() && id <= Last(); }

HRESULTNext(DISPID key, CExpandoObjectSlot*& slot)
{
// zero restarts the enumerator
if (key == 0)
{
// if there are no slots we are done
if (NumDispIds() == 0)
return S_FALSE;

// return the first slot
slot = &m_slots[0];
return NOERROR;
}
else
if (key == Last())
{
// the key was the last slot so we are done
return S_FALSE;
}
else
if (ValidDispId(key))
{
// return the next slot
slot = &m_slots[key-m_dispIdBase+1];
return NOERROR;
}
else
// the key must be invalid
return E_INVALIDARG;
}

////
//
// The local state of the object
//

// the objects reference count
ULONGm_ref;

// the base of objectIds
ULONGm_dispIdBase;

// the hash table of slots - for fast GetIDSofNames lookup
LONGm_hashTable[kSlotHashTableSize];

// the number of slots (and the next dispId to allocate)
UINTm_totalSlots;

// the size of the allocated array of slots
UINTm_slotTableSize;

// a pointer to the allocated array of slots
CExpandoObjectSlot* m_slots;

// controlling unknown
IUnknown *m_punkOuter;

// controlling IDispatch
IDispatch *m_pdisp;
};

#endif // __EXTOBJ_H