📄 idispatcheximpl.h
字号:
//
// 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 + -