Adding and Firing
Events
When something interesting happens in a COM object,
we'd like to be able to spontaneously notify its client without the
client polling the object. COM provides a standard mechanism for
sending these notifications to clients (normally called "firing an
event") using the connection-point architecture.
Connection-point events are actually methods on
an interface. To support the widest variety of clients, an event
interface is often defined as a dispinterface. Choosing
Support Connection Points in the ATL Simple Object Wizard generates
an event in our IDL file. The following is an example of the
wizard-generated code augmented with a single event method (shown
in bold):
[
uuid(B830F523-D87B-434F-933A-623CEF6FC4AA),
helpstring("_ICalcPiEvents Interface")
]
dispinterface _ICalcPiEvents {
properties:
methods:
[id(1)] void OnDigit([in] short nIndex,
[in] short nDigit);
};
In addition to changing the IDL file, the
Support Connection Points option makes several changes to the class
definition. The IConnectionPointContainerImpl base class
is added. This class implements the
IConnectionPointContainer interface, providing
functionality for managing multiple event interfaces on the class.
The IConnectionPointImpl base class implements a
connection point for a specific event interface:
_ICalcPiEvents, in this case. The COM_MAP is also
modified to include an entry for
IConnectionPointContainer, and a new map, the
CONNECTION_MAP, is added to the class.
The wizard also generates a proxy class for the
connection point. This proxy class is added to the base class list
and provides a convenient way to actually fire the events (that is,
call the methods on the connection point). This is very helpful
because the typical connection point is a
dispinterface.
For example:
STDMETHODIMP CCalcPi::CalcPi(BSTR *pbstrPi) {
// (code to calculate pi removed for clarity)
...
// Fire each digit
for( short j = 0; j != m_nDigits; ++j ) {
Fire_OnDigit(j, (*pbstrPi)[j+2] - L'0');
}
...
}
Objects of the CCalcPi class can now send events
that can be handled in a page of HTML:
<object classid="clsid:E5F91723-E7AD-4596-AC90-17586D400BF7"
id=objPiCalculator>
<param name=digits value=50>
</object>
<input type=button name=cmdCalcPi value="Pi to 50 Digits:">
<span id=spanPi>unknown</span>
<p>Distribution of first 50 digits in pi:
<table border cellpadding=4>
... <!- table code removed for clarity >
</table>
<script language=vbscript>
' Handle button click event
sub cmdCalcPi_onClick
spanPi.innerText = objPiCalculator.CalcPi
end sub
' Handle calculator digit event
sub objPiCalculator_onDigit(index, digit)
select case digit
case 0: span0.innerText = span0.innerText + 1
case 1: span1.innerText = span1.innerText + 1
... <! etc >
end select
spanTotal.innerText = spanTotal.innerText + 1
end sub
</script>
The sample HTML page handles these events to
provide the first 50 digits of pi and their distribution, as shown
in Figure 1.11.
For more information about
ATL's support for connection points, see Chapter 9, "Connection Points."
|