Initializing an Event Map

To handle the mapping between events and actions, Patron uses the class CEventMap (EVENTS.CPP). Each instance of CTenant that contains a control will manage an instance of CEventMap, which itself holds an array of EVENTMAP structures. All of these structures are defined as follows in TENANT.H:


//Event actions
typedef enum
{
ACTION_NONE=-1,
ACTION_BEEPDEFAULT=MB_OK,
ACTION_BEEPASTERISK=MB_ICONASTERISK,
ACTION_BEEPEXCLAMATION=MB_ICONEXCLAMATION,
ACTION_BEEPHAND=MB_ICONHAND,
ACTION_BEEPQUESTION=MB_ICONQUESTION,
ACTION_TAILING=-2
} EVENTACTION;

typedef struct tagEVENTMAP
{
DISPID id; //Event ID
EVENTACTION iAction; //Action to take
BSTR bstrName; //Event name (function only)
} EVENTMAP, *PEVENTMAP;

class CEventMap
{
public:
UINT m_cEvents;
LPTYPEINFO m_pITypeInfo;
PEVENTMAP m_pEventMap;

public:
CEventMap(LPTYPEINFO);
~CEventMap(void);

BOOL Init(void);
BOOL Set(DISPID, EVENTACTION);
EVENTACTION Get(DISPID);
void Serialize(LPSTREAM);
void Deserialize(LPSTREAM);
};

typedef CEventMap *PCEventMap;

//Event stream in object storage
#define SZEVENTSSTREAM OLETEXT("\003Event Mappings")

Each event map manages its own serialization and deserialization given any stream. Patron uses the stream name "\003Event Mappings," which it creates in the object's storage. Accordingly, we have to prefix the stream name with ASCII 3 to prevent an object from affecting this stream in any way.

Each EVENTMAP structure in an instance of CEventMap contains the name of an event (for displaying in the Control Events dialog box), the event's dispID, and whatever action is assigned to it. Regardless of the complexity of the container, these three elements will generally be present. A sophisticated container that allows a user to attach code to an event would, for example, replace the iAction field with a pointer to that code and would augment the structure with the names of an event's arguments for use elsewhere in that container's user interface.

You'll notice that the CEventMap constructor takes an ITypeInfo pointer. This pointer is eventually used to initialize the event map in CEventMap::Init. Here we cycle through the member functions of the interface described by ITypeInfo, storing each function name and dispID in a separate EVENTMAP structure (the BSTR name is freed in the CEventMap destructor):


BOOL CEventMap::Init(void)
{
LPTYPEATTR pTA;
UINT i;

if (NULL==m_pITypeInfo)
return FALSE;

if (FAILED(m_pITypeInfo->GetTypeAttr(&pTA)))
return FALSE;

m_cEvents=pTA->cFuncs;
m_pITypeInfo->ReleaseTypeAttr(pTA);

m_pEventMap=new EVENTMAP[m_cEvents];

if (NULL==m_pEventMap)
{
m_cEvents=0;
return FALSE;
}

for (i=0; i < m_cEvents; i++)
{
LPFUNCDESC pFD;

m_pEventMap[i].id=0;
m_pEventMap[i].bstrName=NULL;
m_pEventMap[i].iAction=ACTION_NONE;

if (SUCCEEDED(m_pITypeInfo->GetFuncDesc(i, &pFD)))
{
UINT cNames;
HRESULT hr;

m_pEventMap[i].id=pFD->memid;

hr=m_pITypeInfo->GetNames(pFD->memid
, &m_pEventMap[i].bstrName, 1, &cNames);

m_pITypeInfo->ReleaseFuncDesc(pFD);
}
}

return TRUE;
}

To get this far, of course, we need to retrieve the type information for at least the control's primary event set (the only one Patron uses). This is done through ObjectTypeInfoEvents, an internal function implemented in CONNECT.CPP:


BOOL ObjectTypeInfoEvents(LPUNKNOWN pObj, LPTYPEINFO *ppITypeInfo)
{
HRESULT hr;
LPTYPEINFO pITypeInfoAll;
LPTYPEATTR pTA;

if (NULL==pObj œœ NULL==ppITypeInfo)
return FALSE;

if (!ObjectTypeInfo(pObj, &pITypeInfoAll))
return FALSE;

*ppITypeInfo=NULL; //Use this to determine success.

if (SUCCEEDED(pITypeInfoAll->GetTypeAttr(&pTA)))
{
UINT i;
int iFlags;

for (i=0; i < pTA->cImplTypes; i++)
{
//Get the implementation type for this interface.
hr=pITypeInfoAll->GetImplTypeFlags(i, &iFlags);

if (FAILED(hr))
continue;

if ((iFlags & IMPLTYPEFLAG_FDEFAULT)
&& (iFlags & IMPLTYPEFLAG_FSOURCE))
{
HREFTYPE hRefType=NULL;

pITypeInfoAll->GetRefTypeOfImplType(i, &hRefType);
hr=pITypeInfoAll->GetRefTypeInfo(hRefType
, ppITypeInfo);

break;
}
}

pITypeInfoAll->ReleaseTypeAttr(pTA);
}

pITypeInfoAll->Release();
return (NULL!=*ppITypeInfo);
}

This code employs straight usage of ITypeInfo member functions to look for an interface marked "source" and "default." The ITypeInfo pointer we use comes from another internal function in CONNECT.CPP, ObjectTypeInfo. This function simply queries for IProvideClassInfo and calls IProvideClassInfo::GetClassInfo. Nothing fancy.