⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 idispatcheximpl.h

📁 《脚本驱动的应用软件开发方法与实践》源码
💻 H
📖 第 1 页 / 共 3 页
字号:
//
// IDispatchExImpl.h
//
// --------------------------------------------------------
// A Practical Guide to Script-Driven Software Development
// Author: Qiming Lu                        Date: 6/1/2006
// MSN Messager: luqiming26@hotmail.com
// MSN Blog: http://spaces.msn.com/jemylu
// --------------------------------------------------------

#ifndef __IDispatchExImpl_H__
#define __IDispatchExImpl_H__

#include "IDispatchBaseImpl.h"
#include <map>

// Determine if the user of this class has specified a start value for DISPIDs
#ifndef DISPID_START
#define DISPID_START 0
#endif

class CDynamicDispatchEntry;

template<class DERIVED_CLASS, class BASE_ITF, const IID* BASE_IID>
class __declspec(novtable) IDispatchExImpl : public IDispatchBaseImpl<DERIVED_CLASS, BASE_ITF, BASE_IID>
{
protected:
	// For looking up information
	class BSTRGreater
	{
	public:
		bool operator() (const BSTR str1, const BSTR str2) const
		{
			if (wcscmp(str1, str2) > 0)
				return true;
			else
				return false;
		}
	};

	// typedefs for easier reading
	typedef IDispatchBaseImpl<DERIVED_CLASS, BASE_ITF, BASE_IID> __base;
	typedef std::map<BSTR, CDynamicDispatchEntry*, BSTRGreater>	MapNameToEntry;
	typedef std::map<DISPID, CDynamicDispatchEntry*> MapDispidToEntry;
	
	MapDispidToEntry	m_mapDispidToEntry;
	MapNameToEntry		m_mapNameToEntry;
	MapNameToEntry		m_mapCaselessNameToEntry; // For caseless lookups
	DISPID				m_dispidNext; // The current dispid to add entries at

	// For multi-threaded safety
//	ThreadSafeRefCount	m_csAccessSync;
	

	// Helpers

	/***************************************************************************
	* Function:	IDispatchExImpl::GetEntryByName()
	* Args:		<BSTR> bstrName - the name of the entry to find
	*			<DWORD> dwGrfDex - the flags describing the comparison operator
	* Returns:	<CDynamicDispatchEntry*> the entry that was found or NULL
	* Purpose:	Finds an entry by name and case sensitivity
	***************************************************************************/
	CDynamicDispatchEntry* GetEntryByName(BSTR bstrName,
		DWORD dwGrfDex = fdexNameCaseInsensitive)
	{
		CDynamicDispatchEntry* pEntry = 0;

		// Determine the type of comparison to use
		if (dwGrfDex & fdexNameCaseSensitive)
		{
			// Get the pointer to the entry
			MapNameToEntry::const_iterator it = m_mapNameToEntry.find(bstrName);
			if (it != m_mapNameToEntry.end()) 
			{
				pEntry = (*it).second;
			}
		}
		else
		{
			// Make a copy of the string
			BSTR strTemp = SysAllocString(bstrName);
			// Convert to a consistent case
			_wcslwr(strTemp);
			// Get the pointer to the entry
			MapNameToEntry::const_iterator it = m_mapCaselessNameToEntry.find(strTemp);
			if (it != m_mapCaselessNameToEntry.end()) 
			{
				pEntry = (*it).second;
			}
		}
		return pEntry;
	}

	/***************************************************************************
	* Function:	IDispatchExImpl::CreateNewEntry()
	* Args:		<BSTR> bstrName - the name of the entry to find
	*			<DISPID*> pDispID - the out parameter of the dispid
	*			<VARIANT> vtVal - the starting value of the property
	*			<bool> bTypeInfo - whether this method is contained in the
	*			type info or our map
	*			<bool> bUseInDispid - forces the use of the passed in dispid
	* Returns:	<HRESULT> Standard COM codes
	* Purpose:	Creates a new dispatch entry
	***************************************************************************/
	HRESULT CreateNewEntry(BSTR bstrName, DISPID* pDispID, VARIANT vtVal,
		bool bTypeInfo = false, bool bUseInDispid = false)
	{
		HRESULT hr = E_OUTOFMEMORY;
		
		// Determine if the passed in dispid should be used
		if (bUseInDispid == true)
		{
			// Copy the dispid
			m_dispidNext = *pDispID;
		}

		// Try to allocate a new entry
		CDynamicDispatchEntry* pEntry = 0;
		pEntry = new CDynamicDispatchEntry(bstrName, m_dispidNext, vtVal, bTypeInfo);
		if (pEntry)
		{
			// Add the new entry to the dispid map
			m_mapDispidToEntry[pEntry->GetDispID()] = pEntry;
			// Add it to the case sensitive map
			BSTR strName1 = SysAllocString(bstrName);
			m_mapNameToEntry[strName1] = pEntry;
			// Add it to the caseless map
			BSTR strName2 = SysAllocString(bstrName);
			_wcslwr(strName2);
			m_mapCaselessNameToEntry[strName2]= pEntry;

			// Copy the outbound dispid
			*pDispID = pEntry->GetDispID();
			// Increment the next dispid. This is done here to prevent holes
			// in the dispid range on failed creates
			m_dispidNext = (m_dispidNext < 0) ? 0 : m_dispidNext + 1;
			// Tell the caller that everything worked
			hr = S_OK;
		}
		return hr;
	}

	/***************************************************************************
	* Function:	IDispatchExImpl::GetStartDispID()
	* Args:		<DISPID*> pDispID - the out parameter that gets the dispid
	* Returns:	<HRESULT> Standard COM codes
	* Purpose:	Returns the first dispid in the map. This will look for the first
	*			not deleted entry and return that dispid.
	***************************************************************************/
	HRESULT GetStartDispID(DISPID* pDispID)
	{
		// Default to no entries available
		HRESULT hr = S_FALSE;

		bool bDone = false;
		MapDispidToEntry::iterator it, itEnd(m_mapDispidToEntry.end());
		for (it = m_mapDispidToEntry.begin(); it != itEnd && !bDone; ++it)
		{
			DISPID dispid = -1;

			// Determine if this item is not deleted
			CDynamicDispatchEntry* pEntry = (*it).second;
			if (pEntry->IsDeleted() == false)
			{
				// No need to look further
				bDone = true;
				// Return the dispid
				*pDispID = dispid;
				// Change the HRESULT to S_OK so that the caller does not
				// think that we have no members
				hr = S_OK;
			}
		}
		return hr;
	}

	/***************************************************************************
	* Function:	IDispatchExImpl::GetNextDispID()
	* Args:		<DISPID> dispid - the current dispid to search from
	*			<DISPID*> pDispID - the out parameter that gets the dispid
	* Returns:	<HRESULT> Standard COM codes
	* Purpose:	Returns the nextt dispid in the map. This will look for the first
	*			not deleted entry and return that dispid.
	***************************************************************************/
	HRESULT GetNextDispID(DISPID dispid, DISPID* pDispID)
	{
		// Default to no entries available
		HRESULT hr = S_FALSE;

		// Search until we have no more entries or found the next item
		DISPID curDispid = dispid;
		bool bDone = false;
		while (!bDone)
		{
			// Move to what we think is the next dispid
			curDispid++;

			MapDispidToEntry::const_iterator it = m_mapDispidToEntry.find(curDispid);
			if (it != m_mapDispidToEntry.end()) 
			{
				CDynamicDispatchEntry* const pEntry = (*it).second;
				// Determine if this item is not deleted
				if (pEntry->IsDeleted() == false)
				{
					// No need to look further
					bDone = true;
					// Return the dispid
					*pDispID = curDispid;
					// Change the HRESULT to S_OK so that the caller does not
					// think that we have no members
					hr = S_OK;
				}
			}
			else
			{
				bDone = true;
			}
		}
		return hr;
	}

	/***************************************************************************
	* Function:	IDispatchExImpl::GetVariantByDispID()
	* Args:		<DISPID> dispid - the current dispid to search for
	*			<VARIANT*> pvtResult - the variant that holds the out value
	* Returns:	<HRESULT> Standard COM codes
	* Purpose:	Copies the variant held at the specified dispid
	***************************************************************************/
	HRESULT GetVariantByDispID(DISPID dispid, VARIANT* pvtResult)
	{
		// Clear the destination variant
		HRESULT hr = VariantClear(pvtResult);
		if (SUCCEEDED(hr))
		{
			// Look for the entry corresponding to this dispatch
			MapDispidToEntry::const_iterator it = m_mapDispidToEntry.find(dispid);
			if (it != m_mapDispidToEntry.end()) 
			{
				CDynamicDispatchEntry* const pEntry = (*it).second;
				// Only do the copy if this is a dynamic property
				if (pEntry->UseTypeInfo() == false)
				{
					// Copy the VARIANT from the entry
					hr = pEntry->GetVar(pvtResult);
				}
				else
				{
					// Tell the caller that this dispid is TypeInfo based
					hr = S_FALSE;
				}
			}
			else
			{
				// Unable to find the specified entry
				hr = DISP_E_MEMBERNOTFOUND;
			}			
		}
		return hr;
	}

	/***************************************************************************
	* Function:	IDispatchExImpl::SetVariantByDispID()
	* Args:		<DISPID> dispid - the current dispid to search for
	*			<VARIANT*> pvtResult - the variant that we are to copy
	* Returns:	<HRESULT> Standard COM codes
	* Purpose:	Copies the variant passed in to the entry mapped to the dispid
	***************************************************************************/
	HRESULT SetVariantByDispID(DISPID dispid, VARIANT* pvtResult)
	{
		// Default to unable to find the specified entry
		HRESULT hr = DISP_E_MEMBERNOTFOUND;

		// Look for the entry corresponding to this dispatch
		MapDispidToEntry::const_iterator it = m_mapDispidToEntry.find(dispid);
		if (it != m_mapDispidToEntry.end()) 
		{
			CDynamicDispatchEntry* const pEntry = (*it).second;
			// Only do the copy if this is a dynamic property
			if (pEntry->UseTypeInfo() == false)
			{
				// Set the VARIANT in the entry
				hr = pEntry->SetVar(pvtResult);
			}
			else
			{
				// Tell the caller that this dispid is TypeInfo based
				hr = S_FALSE;
			}
		}
		return hr;
	}

	/***************************************************************************
	* Function:	IDispatchExImpl::AddTypeInfoDispIDs()
	* Args:		None
	* Returns:	<HRESULT> Standard COM codes
	* Purpose:	Uses the TypeInfo interface to add any members that the derived
	*			class needs to expose.
	***************************************************************************/
	HRESULT AddTypeInfoDispIDs(void)
	{
		// Determine the number of methods in the object
		TYPEATTR* pta = 0;
		HRESULT hr = m_pti->GetTypeAttr(&pta);
		if (SUCCEEDED(hr))
		{
			// Loop through getting the information on each member
			for (UINT index = 0; index < pta->cFuncs && SUCCEEDED(hr); ++index)
			{
				// Get the function description for this member
				FUNCDESC* pfd = 0;
				hr = m_pti->GetFuncDesc(index, &pfd);
				if (SUCCEEDED(hr))
				{
					BSTR bstrName = 0;
					// Determine the name of this member
					hr = m_pti->GetDocumentation(pfd->memid, &bstrName, 0, 0, 0);
					if (SUCCEEDED(hr))
					{
						// Look to see if we have already added an entry for
						// the dispid. This happens since get/put properties
						// share the same id.
						MapDispidToEntry::const_iterator it = m_mapDispidToEntry.find(pfd->memid);
						if (it == m_mapDispidToEntry.end()) 
						{
							// Add the member to our dispatch maps
							hr = CreateNewEntry(bstrName, &pfd->memid, VARIANT(), true, true);
						}
					}

					// Release the memory alloced on our behalf
					if (bstrName)
						SysFreeString(bstrName);
					m_pti->ReleaseFuncDesc(pfd);
				}
			}

			// Release the memory alloced on our behalf
			m_pti->ReleaseTypeAttr(pta);
		}
		return hr;
	}

	virtual HRESULT OnTypeInfoLoaded() 
	{
		// We need to determine which dispids the interface already
		// uses to prevent collisions when dynamic properties are
		// added to the object
		AddTypeInfoDispIDs();
		return S_OK;
	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -