#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) { unsigned int strLen; // 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= (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