




已阅读5页,还剩12页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
IntroductionIf you have ever done any development work involving the use of COM objects, chances are you would have encountered the need for COM object event handling. Visual Basic users will know how simple it is to connect with the event interface(s) of the COM (or ActiveX) objects. The VB IDE lays out the event handling function codes nicely for the user. All the user has to do is to fill in the details of the event handling functions.For Visual C+ users, this is not always so straight forward. If your COM object happens to be an ActiveX control and you are using MFC, then yes, the Visual C+ IDE provides Wizards that can help you generate event handler function stubs. All the necessary codes (e.g. inserting the event sink map and event entry macros) are done automatically for you.But what if your COM object is not an ActiveX control? What if you are using straight COM objects which also fire events and you need to handle those events?If you are an MFC user, you may want to tinkle with the various MFC macros to see if you can fit in an event handler function into your code either manually or via the Wizard. I personally believe this is possible. But you need to be armed with an intimate knowledge of MFC and its generated macros.If you do not use MFC, you may want to experiment with ATL codes (e.g.IDispEventImpl,BEGIN_SINK_MAP,SINK_ENTRY_EX, etc.) to perform event handling. The ATL macro codes are certainly not simple but they are well-documented in MSDN and they do provide standard handling mechanisms.In this article, I will go back to basics and seek to explain the fundamental principles of how event handling is done in COM. I will also provide a C+ class which serves as a basic and simple (at least in terms of code overhead) facilitator for COM Object Event Handling.I do this via a special custom-developed template class namedTEventHandlerwhich I have used in many projects. This class uses COM first principles and primitives and avoids the use of complicated macros. The sections following expound this class in detail. I assume that the reader is suitably conversant with C+, ATL and the concepts of template classes. However, before we start discussing theTEventHandlerclass, let us explore the fundamental principles of Event Handling in COM.Event Handling in COMIncoming InterfacesWhen we develop our usual COM objects, we provide implementations for interfaces (defined in an IDL file) which we write ourselves or have been supplied to us. Such implementations facilitate what are known as incoming interfaces. By incoming, we imply that the object listens to its client. That is, the client calls methods of the interface and, in this way, talks to the object.Referring to the diagram below, we can say thatISomeInterfaceis an incoming interface provided by the COM object on the right.Outgoing InterfacesAsKraig Brockschmidtputs it so well in his book Inside OLE, many COM objects themselves have useful things to say to their clients. And clients may want to listen to COM objects too. If such a two-way dialog is desired, something known as an outgoing interface is required. The term outgoing is used in the context of the COM object. It is outgoing in the perspective of the COM object. Imagine a situation in which the role of talker and listener is reversed as shown in the diagram below:Referring to the diagram above, we can say thatITalkBackInterfaceis an outgoing interfacesupportedby the COM object on the right. The COM object invokes the methods ofITalkBackInterfaceand it is theClientthat implements theITalkBackInterfacemethods. A COM object that supports one or more outgoing interfaces is known as aConnectable Objector aSource. A connectable object can support as many outgoing interfaces as it likes. Each method of the outgoing interface represents a singleeventorrequest. All COM objects, regardless of whether they are non-visual COM objects or ActiveX controls (generated manually or via MFC or ATL), use the same mechanism (connectability) for firing events to their clients.Events and RequestsEvents are used to tell a client that something of interest has occurred in the object - a property has changed or the user has clicked a button. Events are particularly important for COM controls. Events are fired by COM objects and no response from the client is expected. In other words, they are simple notifications. Requests, on the other hand, is how a COM object asks the client a question and expects a response in return. Events and requests are similar to Windows messages, some of which inform a window of an event (e.g.WM_MOVE) and some will ask for information from the window (e.g.WM_QUERYENDSESSION).SinksIn both cases, the client of the COM object must listen to what the object has to say and then use that information appropriately. It is the client, therefore, that implements the outgoing interfaces which are also known assinks(I really dislike this name but it has become common and ubiquitous in the world of COM and .NET). From a sinks perspective, this outgoing interface is actuallyincoming. The sink listens to the COM object through it. In this context, the connectable COM object plays the role of a client.How Things are Tied Up TogetherLet us take a helicopter view of the entire communications situation. There are three participants: The COM object itself The Client of the COM object The SinkThe Client communicates with the COM object as usual via the objects incoming interface(s). In order for the COM object to communicate back to the client in the other direction, the COM object must somehow obtain a pointer to an outgoing interface implementedsomewherein the client. Through this pointer, the COM object will send events and requests to the client. Thissomewhereis the Sink. Let us illustrate the above with a simple diagram:Note that the Sink is an object by itself. The Sink provides the implementation for one or more outgoing interfaces. It is also usually strongly tied to the other parts of the clients code because the whole idea of implementing a sink is for the client code to be able to react to an event and/or to respond to some request from the COM object.How Does a COM Object Connect to a Sink?But how does a COM object connect to a Sink in the first place? This is where the notion of Connection Points and Connection Point Containers come in. We will explore this in detail in the next section.Connection Points and Connection Point ContainersFor each outgoing interface that a COM object supports (note the use of the word support; the COM object itself does notimplementthis interface, but rather,invokesit), the COM object exposes a small object called aconnection point. This connection point object implements theIConnectionPointinterface.It is through thisIConnectionPointinterface that the client passes its Sinks outgoing interface implementation to the COM object. Reference counts of theseIConnectionPointobjects are kept by both the client and the COM object itself to ensure the lifespan of the two-way communications.The method to call (from the client side) to establish event communication with the COM object is theIConnectionPoint:Advise()method. The converse ofAdvise()isIConnectionPoint:Unadvise()which terminates a connection. Please refer to MSDN documentation for more details of these methods.Hence, viaIConnectionPointinterface method calls, a client can start listening to a set of events from the COM object. Note also that because a COM object maintains a separateIConnectionPointinterface for every outgoing interface it supports, a client must be able to use the correct connection point object for every sink it implements.How then, does the Client choose the appropriate connection point for a sink? In comesConnection Point Containers. An object which is connectable must also be a Connection Point Container. That is, it must implement theIConnectionPointContainerinterface. Through this interface, a Client requests for the appropriate Connection Point object of an outgoing interface.When a client wants to connect a Sink to a Connection Point, it asks the Connection Point Container for the Connection Point object for the outgoing interface implemented by that Sink. When it receives the appropriate connection point object, the Client passes the Sinks interface pointer to that connection point. TheIConnectionPointContainerinterface pointer itself can be obtained easily viaQueryInterface()on the COM object itself. Nothing speaks better than an example code which is listed below:void Sink:SetupConnectionPoint(ISomeInterface* pISomeInterface) IConnectionPointContainer* pIConnectionPointContainerTemp = NULL; IUnknown* pIUnknown = NULL; /*QI this object itself for its IUnknown pointer which will be used */ /*later to connect to the Connection Point of the ISomeInterface object.*/ this - QueryInterface(IID_IUnknown, (void*)&pIUnknown); if (pIUnknown) /* QI pISomeInterface for its connection point.*/ pISomeInterface - QueryInterface (IID_IConnectionPointContainer, (void*)&pIConnectionPointContainerTemp); if (pIConnectionPointContainerTemp) pIConnectionPointContainerTemp - FindConnectionPoint(_uuidof(ISomeEventInterface), &m_pIConnectionPoint); pIConnectionPointContainerTemp - Release(); pIConnectionPointContainerTemp = NULL; if (m_pIConnectionPoint) m_pIConnectionPoint - Advise(pIUnknown, &m_dwEventCookie); pIUnknown - Release(); pIUnknown = NULL; The sample function above describes theSetupConnectionPoint()method of aSinkclass. TheSinkclass implements the methods of the outgoing interfaceISomeEventInterface. TheSetupConnectionPoint()method takes a parameter which is pointer to an interface namedISomeInterface. The COM object behindISomeInterfaceis assumed to be a Connection Point Container. The following is an outline of the functions logic:1. We firstQueryInterface()theSinkobject itself for itsIUnknowninterface pointer. ThisIUnknownpointer will be used later in the call toIConnectionPoint:Advise().2. Having successfully obtained theIUnknownpointer, we nextQueryInterface()the object behindpISomeInterfacefor itsIConnectionPointContainerinterface.3. Having successfully obtained theIConnectionPointContainerinterface pointer, we use it to find the appropriate Connection Point object for the outgoing interfaceISomeEventInterface.4. If we are able to obtain this Connection Point object (it will be represented bym_pIConnectionPoint), we will proceed to call itsAdvise()method.5. From here onwards, whenever the COM object behindpISomeInterfacefires an event to the sink (by calling one of the methods ofISomeEventInterface), the corresponding method implementation in theSinkobject will be invoked.I certainly hope that the above introductory sections on Event Handling in COM, Connection Points and Connection Point Containers will have served to provide the reader with a clear understanding of the basics of event handling. It is worth re-mentioning that the above principles are used whatever the type of COM object is involved (e.g. straight COM object, ActiveX controls, etc). With the basics explained thoroughly, we shall proceed to expound on the sample source codes, especially theTEventHandlertemplate class.The Sample Source CodeI will attempt to explainTEventHandlerby running through some example codes. Please refer to the source files which are contained in theTEventHandler_src.zipfile. In the set of sample codes, I have provided two sets of projects: EventFiringObject TestClientEventFiringObjectThe EventFiringObject project contains the code for a simple COM object which implements an interface namedIEventFiringObject. This COM object is also a Connection Point Container which recognizes the_IEventFiringObjectEventsconnection point. The relevant IDL constructs for this COM object is listed below for discussion purposes: object, uuid(8E396CC0-A266-481E-B6B4-0CB564DAA3BC), dual, helpstring(IEventFiringObject Interface), pointer_default(unique)interface IEventFiringObject : IDispatch id(1), helpstring(method TestFunction) HRESULT TestFunction(in long lValue); uuid(32F2B52C-1C07-43BC-879B-04C70A7FA148), helpstring(_IEventFiringObjectEvents Interface)dispinterface _IEventFiringObjectEvents properties: methods: id(1), helpstring(method Event1) HRESULT Event1(in long lValue); uuid(A17BC235-A924-4FFE-8D96-22068CEA9959), helpstring(EventFiringObject Class)coclass EventFiringObject default interface IEventFiringObject; default, source dispinterface _IEventFiringObjectEvents;In the EventFiringObject project, we implement a C+ ATL class namedCEventFiringObjectwhich implements the specifications ofcoclassEventFiringObject.CEventFiringObjectprovides a simple implementation of theTestFunction()method. It simply firesEvent1which is specified in_IEventFiringObjectEvents.STDMETHODIMP CEventFiringObject:TestFunction(long lValue) /* TODO: Add your implementation code here */ Fire_Event1(lValue); return S_OK;TestClientTestClient is a simple test application: an MFC dialog-based application which instantiates theEventFiringObjectCOM object. It also attempts to handle theEvent1event fired fromEventFiringObject. I will walk through the TestClient code more thoroughly to explain the process of event handling. The client code centers around theCTestClientDlgclass which is derived fromCDialog. InTestClientDlg.h, notice that we declare an instance of a smart pointer object which will be tied to the COM object which implementsIEventFiringObject:/* * Declare an instance of a IEventFiringObject smart pointer. * */ IEventFiringObjectPtr m_spIEventFiringObject;Then, in theCTestClientDlg:OnInitDialog()function, we instantiatem_spIEventFiringObject:/* * Create an instance of an object which implements IEventFiringObject. * */ m_spIEventFiringObject.CreateInstance(_uuidof(EventFiringObject);We also create a button in our simple dialog box labeled Call Test Function. In the click handler for this button, we invoke theTestFunction()of ourm_spIEventFiringObject:/* * Call the IEventFiringObject.TestFunction(). * */ /* * This will cause the object which implements * */ /* * IEventFiringObject to fire Event1. * */ m_spIEventFiringObject - TestFunction(456);Thus far, we have dealt with mostly typical COM client code. The fun begins when we invokeTestFunction(). We know thatTestFunction()will cause them_spIEventFiringObjectCOM object to fire theEvent1event. This is where the real action starts.The TEventHandler ClassGeneral Design Goals and Example Use CaseTheTEventHandlerclass is supplied inTEventHandler.h. It works according to the following design: It serves the role of a Sink for a client. It generically handles one event interface which must be dispinterface-based (i.e. derived fromIDispatch). After receiving an event fired from the COM object,TEventHandlerwill call a predefined method of its client. This is howTEventHandlerclients get notified of events fired from the COM object.The predefined method of the client must be defined using the following signature:typedef HRESULT (event_handler_class:*parent_on_invoke)( TEventHandler* pthis, DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr);Notice that the predefined methods parameters match those ofIDispatch:Invoke()except for the addition of a parameter (first in the list) which is a pointer to theTEventHandlerinstance itself. This parameter is supplied as it may be useful to client code. In the context of our example code, our usage ofTEventHandlercan be represented by the following diagram:What TEventHandler is Not Designed to DoWhy do we make an apparent rehash of theIDispatch:Invoke()method? What value-add couldTEventHandlerhave if your callback function still has to handle all the parameters of theIDispatch:Invoke()call?The answer is that theTEventHandlerclass is not primarily designed to simplify the handling of the parameters of the event methods (although this might be possible, see my comments on this later in this section).It is designed to be asinkobject. It is meant to readily make available (for a client) a sink which can be hooked up to receive the dispinterface-based event of a COM object. And then have the sink automatically call the mirrorInvoke()method of the client.Note that using C+ templates alone, it will not be possible to anticipate in advance the return values and parameter lists of event methods. This would require the work of Wizards which can read all these information from the type libraries associated with the connectable COM objects and then generating function codes which match the signatures of event methods.TEventHandleris not a wizard. It is a C+ template (which makes itsort ofa bona-fide code generator, but it cant do everything, e.g. read a type library and generate code according to information found therein.). It is also meant to be a Sink for one event interface which must be derived fromIDispatch, and henceTEventHandlerimplementsIDispatch(see the next sectionWhy Is TEventHandler Dispinterface-based?for more information on the reasons behind this).Why is TEventHandler Dispinterface-based?In order forTEventHandlerto be a Sink forI
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 南宁代建合同范本
- 简短个人租房合同范本
- 门窗采购合同范本
- 路面硬化劳务合同范本
- 成人交友活动合同
- 合肥装潢公司合同范本
- 保险销售的合同范本
- 电缆敷设合同范本
- 工程小活合同范本
- 农具机械购销合同范本
- 2024年中考数学真题分类汇编(全国版)专题12一次函数及其应用(39题)含答案及解析
- 2024城市轨道交通节能改造EMC合作合同
- 全国职业院校技能大赛中职(大数据应用与服务赛项)考试题及答案
- 实验室检验结果及报告管理制度
- 新能源汽车动力系统优化
- 2022年版 义务教育《数学》课程标准
- 《电力行业职业技能标准 农网配电营业工》
- JTG∕T F30-2014 公路水泥混凝土路面施工技术细则
- 第四章 休克病人的护理课件
- 委托合同解除协议书
- 植物生理学课件(王小菁-第8版)-第五章-植物同化物的运输
评论
0/150
提交评论