![]() |
COM provides connection points as a standard mechanism for connecting event sources to event sinks. Although they provide a useful standard that can be used between event sources and sinks that have been separately developed, they are expensive (in terms of interface calls) and are therefore normally used only for in-process components .
Attributes can be used to implement connection points by using the event_source attribute with the com parameter. For example:
[coclass,event_source(com)] classMyClass:publicISomeInterface { };
Note that event_source isnt used only in COM code. This attribute can be used to generate three different kinds of events, depending on the first parameter:
com is used to generate COM connection points.
native is used to generate events using C++ callbacks.
managed is used to generate .NET events and can be used only with managed classes.
Because the syntax is similar for all three types, Microsoft has rather grandly termed this the Unified Event Model . As were dealing with implementing COM objects in this chapter, Ill be considering only the first type.
It is easy to add event support if you are creating an ATL object with Visual Studio .NET. Simply check the Connection Points box on the Options page of the ATL Object Wizard. The following paragraphs show you what gets added to an ATL project to support connection points.
The first item that has been added to the generated code is an event_source attribute on the implementation class, showing that the class supports COM connection points:
[ coclass, threading("apartment"), event_source("com"), vi_progid("SimpleJ.SimpleK"), progid("SimpleJ.SimpleK.1"), version(1.0), uuid("C57EA18F-2869-4CD8-BB36-7743B63F718E"), helpstring("SimpleKClass") ] classATL_NO_VTABLECSimpleK: publicISimpleK {...
A coclass that implements connection points also has to support a source interface, so youll see that the wizard has added a dispinterface to the file:
//_ISimpleKEvents [ dispinterface, uuid("5D2B0BD7-5CD8-46E0-9855-2F6B898D0B0C"), helpstring("_ISimpleKEventsInterface") ] __interface_ISimpleKEvents { };
The dispinterface event is associated with the coclass using the __event keyword:
classATL_NO_VTABLECSimpleK: publicISimpleK { public: CSimpleK() { } __event__interface_ISimpleKEvents; ... };
This declaration associates _ISimpleEvents as a source interface for the class and will also cause the provider to generate a method that you call to fire the event. You can now add the definitions of the event methods to dispinterface :
__interface_ISimpleKEvents { //Returntwovalueswhenasked [id(1),helpstring("methodValues")] voidValues([in]shortx,[in]shorty); };
When you compile the project with the /Fx option set and look at the .mrg file, youll see the compiler has generated a Values method as part of the coclass implementation. To fire this method, you simply call it, and because it has the same name and arguments as the dispinterface method, it doesnt matter that the method is hidden from you.
You can also use the __raise keyword when firing events:
__raiseValues(10,10);
This keyword emphasizes the site of an event, to quote MSDN. It is optional and has two effects: it makes it easy to see where events are being raised; and it causes a runtime error if it is used with a function that isnt an event. The second effect helps guard against accidentally calling the wrong function.
The following code shows how to define a COM class that fires events if you are not using Visual Studio .NET. Listing 6-4 contains the header file, EvtSrc.h, which defines the GUID for a class called CEventSource . Youll find this file in the Chapter06\Events\Src folder in the books companion content.
![]() |
#pragmaonce [dual,uuid("d4efa6dc-bb8f-44f0-88cf-2ae663c76312")] __interfaceIEvents{ [id(1)]HRESULTValues([in]shortnVal1,[in]shortnVal2); }; [dual,uuid("a3b7fea2-7396-4727-9691-7dc55acca27a")] __interfaceISource{ [id(1)]HRESULTFire(); }; classDECLSPEC_UUID("530DF3AD-6936-3214-A83B-27B63C7997C4")CEventSource;
![]() |
CEventSource is a COM class that supports one interface, ISource , and one event interface, IEvents . The ISource interface has one member, Fire , which is used to raise the event defined in the IEvents source interface. The EvtSrc.cpp file implements this COM class in Listing 6-5. This file is also located in the Chapter06\Events\Src folder in books companion content.
![]() |
#define_ATL_ATTRIBUTES1 #include<atlbase.h> #include<atlcom.h> #include"EvtSrc.h" [module(DLL,name="EventSource",uuid="6E46B59E-89C3-4c15-A6D8-B8A1CEC98830")]; [coclass,event_source(com),uuid("530DF3AD-6936-3214-A83B-27B63C7997C4")] classCEventSource:publicISource{ public: __event__interfaceIEvents; //Thismethodfirestheevent HRESULTFire(){ __raiseValues(10,12); returnS_OK; } };
![]() |
The first thing to note is that to define events in this way, you need to include the ATL header files and define the _ATL_ATTRIBUTES preprocessor symbol. The CEventSource class defines an event member and uses the optional __raise keyword to fire the event when the Fire method is called.
You can build this code into a DLL and register the COM coclass by using the following command lines:
cl/LDEvtSrc.cpp regsvr32EvtSrc.dll
In the next section, youll see how to handle the events raised by this component.
A class that is going to handle connection point events should be created with the event_receiver attribute. Like event_source , this attribute takes a com parameter to show that it wants to use connection points rather than native or managed events. The class also needs to implement the methods in the sink interface, which in practice means defining handler methods that have the same signature. Note that the class does not have to derive from the interface directly; the handler methods will be dynamically hooked up to the source object at run time.
Listing 6-6 shows how to handle the event fired by the CEventSource class that was defined in the previous section. You can find this file in the Chapter06\Events\Client folder in the books companion content.
![]() |
#define_ATL_ATTRIBUTES1 #include<atlbase.h> #include<atlcom.h> #include<iostream> usingnamespacestd; #include"..\Src\EvtSrc.h" //Defineamodule [module(name="EventTest")]; //Defineaneventhandlerclass [event_receiver(com)] classEventHandlerClass{ public: HRESULTHandler1(shortnVal1,shortnVal2){ cout<<"Handler1calledwithvalues"<<nVal1<<"and" <<nVal2<<endl; returnS_OK; } HRESULTHandler2(shortnVal1,shortnVal2){ cout<<"Handler2calledwithvalues"<<nVal1<<"and" <<nVal2<<endl; returnS_OK; } //Hookuptwohandlerstothesameeventsource voidHookEvent(ISource*pSource){ __hook(&IEvents::Values,pSource, &EventHandlerClass::Handler1); __hook(&IEvents::Values,pSource, &EventHandlerClass::Handler2); } //Unhooktheeventhandlers voidUnhookEvent(ISource*pSource){ __unhook(&IEvents::Values,pSource, &EventHandlerClass::Handler1); __unhook(&IEvents::Values,pSource, &EventHandlerClass::Handler2); } }; intmain(){ //CreateCOMobject CoInitialize(NULL); ISource*pSource=0; HRESULThr=CoCreateInstance(__uuidof(CEventSource),NULL, CLSCTX_ALL,__uuidof(ISource),(void**)&pSource); if(FAILED(hr)){ cout<<"ErrorcreatingCEventSourceobject:"<<hex <<hr<<dec<<endl; return-1; } //Createthehandlerobject,andsetuptheeventnotification EventHandlerClasstheHandler; theHandler.HookEvent(pSource); //Firetheevent pSource->Fire(); //Unadvise theHandler.UnhookEvent(pSource); CoUninitialize(); return0; }
![]() |
The code defines a class that is going to be used to handle COM events and which is therefore tagged with the event_receiver(com) attribute.
Two new keywords are used to handle events: __hook and __unhook . The __hook keyword generates an advise call to the object, telling it to start sending events, while __unhook undoes the advise. To use __hook , you need to pass three things:
The address of the event. When handling events from a COM object, this must be the address of an event function on an interface, not on the COM class itself.
A pointer to the source object.
The address of the handler function.
Once the call to __hook has returned, the handler function will be called each time a Values event is fired. To stop being sent events, __unhook is called with the same parameters. In this example, two handler functions are hooked to the same event to show its possible to have more than one handler function for an event.
![]() |