Original: http://vckbase.com/index.php/wv/1256.html
First, preface
The last book introduced the callback interface, on the basis of which we understand the connection point is much easier.
Second, the principle
Diagram One, the connection point component schematic diagram. Left side is client, right side is service side (Component object)
It looks so complicated ... Oh, in fact, simple tight: (Note 1)
1, a COM component that allows multiple connection point objects (IConnectionPoint).
This means that there can be multiple sources of "events" happening. There are 3 points of connection;
2. The interface for managing these connection points is called a "Connection point container" (IConnectionPointContainer).
The connection point container interface is particularly simple, because there are only 2 functions, one is FindConnectionPoint (), which means finding the connection point you want, and the other is EnumConnectionPoints (), which indicates that all the connection points are listed, and then you choose which one to use. In practical applications, the lookup method uses the most, accounting for 90%, while the enumeration method uses only 10%, generally in support of third-party plug-ins (Plug in). (Do you want to write a plugin for IE?) We'll talk about it later.)
3, each connection point, can be multiple clients of the receiver (Sink) connection;
We are familiar with this, remember we in the last book in order to manage multiple callback interface, using a cookie way to make a difference?!
III. Realization of components (I.)
1. Establish a working area (WorkSpace)
2. In the workspace, create an ATL project (project). The project name in the sample program is called SIMPLE15, which accepts all default options.
3, ClassView, execute the right mouse button menu command New Atl Object ..., add the ALT class.
4, the left classification category Select Objects, right Objects select Simpleobject (In fact, the default project).
5. Name in the card, enter the component name. The sample program is Dispconnect.
6, attribute Attributes card, interface type select Dual Dual interface. Note Be sure to select Support Connection Points for connection points.
7, ClassView, select Interface (Idispconnect), the right mouse button menu Add Function ...
8, add the function. As with the previous program, add an interface function to compute the addition, but return the result of the calculation through the connection point interface.
9, the following should increase the "event" function. Select the event interface (_idispconnectevents) and add the function.
10. The function is used to return the calculation result of the ADD () function.
11, switch to FileView card, compile IDL file. Of course you can also compile all the projects directly. In fact, the purpose of the compilation is to generate a TLB file from the IDL file, because the VC IDE environment only knows the TLB, the following "Event proxy class program code" can be generated.
12. Generate Event proxy class program code. Select the Component Class object (Cdispconnect), perform the right mouse button menu "Implement connection Point"
13. Select the agent code that you want the IDE to help you build which connection point. We have only one connection point for this component, so we have to choose it. (in example two, we need to implement two connection points, at that time, you will have to choose two)
14, to this, the VC IDE finally help us to complete all the framework, the following we write the real task code.
STDMETHODIMP Cdispconnect::add (Long N1, long N2) {Long nval = n1 + n2; Fire_result (nval);//Call the IDE to help us generate the proxy function code, issue the event return S_OK;}
15. Correct errors in the code generated by the IDE. You don't have to memorize the wrong points, just compile them and you'll make a mistake. General VC6 help us generate the code, there are 2 places may be bug. One is to open the header file, find the connection point mapping macro, modified as follows:
Begin_connection_point_map (Cdispconnect) <b>connection_point_entry (diid__idispconnectevents) </b>// Modify Iid_xxxx to Diid_xxxxend_connection_point_map ()
This error is hateful, since we are using a dual interface connection point, the code generated by it will not be judged? Another possible error can occur in the fire_xxxx () function in the proxy class. In the example program of the Fire_result () function code, we read it by ourselves, simply by iterating through the interface pointers for each and every object that the cookie represents, (if it is an Automation interface, then get the IDispatch interface pointer), and then invoke the event function. You don't understand that it's not much of a relationship right now, but in the next example, it gives us the code that is wrong and we need to modify it. This is something, I'll talk about it later.
Iv. Implementing the Caller (i)
1. Build an MFC project (project). The project name in the sample program is called use.
2, according to the knowledge we learned before, add #import, AfxOleInit () 、...... Not much waste of tongue. If you do not, then please re-read it again from "fourth back". (Note 2)
3. Here are just a few highlights. We need to add the "sink" object in the caller's project. Remember the way you added the callback receiver object to the last book? On the last, our callback interface was inherited from IUnknown. In this case, because our component is a dual interface (Dual), the connection point is also a dual interface, so this time our receiver will be derived from IDispatch.
4, complete the CSink class interface function (virtual function)
STDMETHODIMP Csink::queryinterface (const struct _GUID &iid,void * * PPV) {*ppv=this;return S_OK;} ULONG __stdcall csink::addref (void) {return 1;} Just make a fake, because this object will not quit before the program ends. ULONG __stdcall Csink::release (void) {return 0;} Make a fake, because this object will not exit before the end of the program Stdmethodimp csink::gettypeinfocount (unsigned int *) {return E_NOTIMPL;} Do not have to implement, anyway do not stdmethodimp csink::gettypeinfo (unsigned int,unsigned long,struct ITypeInfo *) {return E_NOTIMPL;} Do not have to implement, anyway do not stdmethodimp csink::getidsofnames (const struct _GUID &,unsigned short * *, unsigned int,unsigned long, Long *) {return E_NOTIMPL;} Do not have to implement, anyway do not stdmethodimp Csink::invoke (long dispid,const struct _guid &,unsigned long,unsigned short,struct Tagdispparams * pparams,struct tagvariant *,struct tagexcepinfo *,unsigned int *) {//Just implement this is enough switch (DISPID)// Depending on the DispID, complete the different callback function {case 1:......//here to receive the event that COM emitted break;case 2:......//event code DISPID is actually the ID of the connection point function in the IDL file (n) The number Break;default:break;} return S_OK;}
Five , Example (ii)
The 2nd component in the sample program (Multconnect), we add one more connection point (_IDISPCONNECTEVENTS2). This interface object is responsible for completing a clock that emits a "clock event" to the caller at every interval of milliseconds. Adding a second connection point is to manually modify the IDL file
The Library Multconnectlib{importlib ("Stdole32.tlb"), Importlib ("Stdole2.tlb"), ...//First, ATL The framework defaults to our generated connection point interface description <b> [//need to manually add a second or more connection point uuid (F81DB93F-4F63-4A55-8114-A32BC78466D3),//CLSID can be used with GUIDGEN. EXE to generate helpstring ("_idispconnectevents2 Interface")]dispinterface _idispconnectevents2{properties:methods:};</ b> [UUID (9461BE82-0D64-4E3B-B0DB-2306D1BFE3F0),//This is the type library ID of the sample program and certainly not the same as the one you generated helpstring ("Dispconnect Class")] CoClass Dispconnect{[default] Interface idispconnect;[ Default, source] dispinterface _idispconnectevents; <b>[source] dispinterface _idispconnectevents2; Don't forget, there's another line here. </b>};
Well, as in the previous way, add interface functions, compile IDL files, let the IDE help us implement proxy class code, enter program code, and modify the bug in the framework code. In the example, our event function is called HRESULT Timer ([in] VARIANT Vardata), which passes information about a time type (Vt_data) in Vardata (note 3). Let's take a look at the error in the proxy class code:
HRESULT Fire_timer (VARIANT vardate) { ccomvariant varresult; t* PT = static_cast (this); int nconnectionindex; ccomvariant* pvars = new ccomvariant[1]; int nconnections = M_vec. GetSize (); for (nconnectionindex = 0; Nconnectionindex < nconnections; nconnectionindex++) { pt->lock (); CComPtr sp = M_vec. GetAt (Nconnectionindex); Pt->unlock (); idispatch* pdispatch = reinterpret_cast (SP.P); if (pdispatch! = NULL) {variantclear (&varresult); <b>//original code, this is Pvars[0]=&vardata? Stupid! You have to modify it yourself. Pvars[0] = vardate;</b> Dispparams disp = {pvars, NULL, 1, 0}; Pdispatch->invoke (0x1, Iid_null, Locale_user_default, Dispatch_method, &disp, &varresult, NULL, NULL); } } Delete[] Pvars; return varresult.scode;}
In terms of writing caller client code, if you need to receive clock events, you can emulate the example to derive a clock sink from IDispatch repeatedly. We download the case program code, which contains a wealth of explanatory information.
Vi. Summary
Connection points, especially the two-interface connection points, are less efficient to run on a remote (DCOM) environment. If you only want to complete the simple "notifications" feature, then the callback interface in the previous one is a sensible scenario and can be run on a DCOM environment. Connection point scenarios are of course also important because many Microsoft applications (IE, Office ...) All support connection points, and ActiveX can only provide "events" functionality through the connection point interface. So, we are all in good hands. Good and good ...
Note 1: Jin Yong's martial arts novels, always use "xx tight" to express "very xx." I also learn to learn, hehe.
NOTE 2: If you have seen it several times, your ladyship will not be able to do so. Don't learn it first. 5555
Note The 3:data type is a 8-byte double, and its integer portion represents the total number of days starting December 30, 1899, and the fractional part indicates how many points of the day the day has elapsed. This time type, represented by a variant, is the vt_date type, which is represented by COleDateTime in MFC. There is a demonstration of this type of operation in the sample program.
"Reprint" COM Component design and application (15)--Connection point (vc6.0)