490 lines
9.2 KiB
C++
490 lines
9.2 KiB
C++
#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_DISPIDS 250
|
|
#define NUM_CORE_DISPIDS 250
|
|
#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 = (LONG)(this - base);
|
|
return this;
|
|
}
|
|
|
|
private:
|
|
// the DispId
|
|
DISPID m_dispId;
|
|
// the name
|
|
LPOLESTR m_name;
|
|
// the name hash
|
|
ULONG m_hash;
|
|
// the property value
|
|
VARIANT m_value;
|
|
// the hash bucket link (index based)
|
|
LONG m_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
|
|
HRESULT 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
|
|
//
|
|
|
|
UINT NumDispIds()
|
|
{ return m_totalSlots; }
|
|
|
|
DISPID First()
|
|
{ return m_dispIdBase; }
|
|
|
|
DISPID Last()
|
|
{ return m_totalSlots + m_dispIdBase - 1; }
|
|
|
|
BOOL ValidDispId(DISPID id)
|
|
{ return id >= First() && id <= Last(); }
|
|
|
|
HRESULT Next(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
|
|
ULONG m_ref;
|
|
|
|
// the base of objectIds
|
|
ULONG m_dispIdBase;
|
|
|
|
// the hash table of slots - for fast GetIDSofNames lookup
|
|
LONG m_hashTable[kSlotHashTableSize];
|
|
|
|
// the number of slots (and the next dispId to allocate)
|
|
UINT m_totalSlots;
|
|
|
|
// the size of the allocated array of slots
|
|
UINT m_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
|