- Basic Principles of connectable objects and connection points
In order to provide greater interaction between the component object and the customer, the component object also needs to actively communicate with the customer. The Component Object communicates with the customer through the outgoing interface. If a component object defines one or more output interfaces, this component object is called a connectable object.
The so-called outbound interface is also a COM interface. Each outbound Interface contains a group of functions. Each member function represents an event, a notification, or a request. However, these interfaces are implemented in the customer's event receiver (sink), so they are called. The event receiver is also a COM object.
The connectable object must implement an iconnectionpointcontainer interface to manage all outgoing interfaces. Each outbound interface corresponds to a connection point object, which implements the iconnectionpoint interface. The customer establishes a connection with the connectable object through the iconnectionpoint interface. Each connection is described in the connectdata structure.
Connectdata contains two members: iunknown * punk and DWORD dwcookie. Punk corresponds to the iunknown interface pointer of the event receiver in the customer. dwcookie is a 32-bit integer generated by the connection point object that uniquely identifies the connection.
You can access all the connection points of a connectable object through ienumconnectionpoints. However, to obtain the ienumconnectionpoints interface pointer, you must use the iconnectionpointcontainer: enumconnectionpoints (ienumconnectionpoints **) function. This function returns the enumerator interface pointer.
You can use ienumconnections, an enumerator interface with connectable objects, to access all connections at a connection point. You can use the iconnectionpoint: enumconnections (ienumconnections **) function to obtain the ienumconnections interface pointer.
To sum up, A connectable object must implement four interfaces: iconnectionpointcontainer, iconnectionpoint, ienumconnectionpoints, and ienumconnections. For the definition of these four interfaces, please read the msdn documentation.
Now, we will briefly describe the communication process between the connectable object and the customer based on the following examples. In the following example, the connectable object connobject defines the output interface ieventsink, which corresponds to this output interface, A connection point object sampleconnpoint is implemented (this object implements the iconnectionpoint interface corresponding to the outbound interface, and the interface ID is iid_ieventsink ).
- The customer calls
M_piunknown-> QueryInterface (iid_iconnectionpointcontainer, (void **) & pconnptcont); if the call is successful, pconnptcont will store the iconnectionpointpointiner interface pointer of the connection object. If the call fails, the object is not a connectable object.
- Call pconnptcont-> findconnectionpoint (iid_ieventsink, & pconnet ). If you call
Pconnpoint stores the iconnectionpoint pointer of the Connection Point Object sampleconnpoint corresponding to the output interface ieventsink. If the call fails, the connection object does not support the output interface ieventsink.
- Call pconnp-> advise (pieventsink, & m_dwcookie) to create an event sink)
Connection to the connection point. Pieventsink is the pointer to the iunknown interface of the customer event receiver. This pointer is passed to the connectable object through this function to initiate communication to the customer. m_dwcookie is the connection ID, this value is saved by the customer by the connectable object settings. The customer also uses this value to disconnect the connection.
- A connectable object can call a method in the client event receiver through a connection point. Customer and connection point successful
After the connection is established, the pointer of the client event receiver interface has been saved in the connection point and can be obtained by calling pconnp-> getconnections.
- The customer calls pconnp-> unadvise (m_dwcookie) to cancel the connection, and CALLS pconnp-> release () to release the connection point object.
- Programming example
Now we use MFC to implement a connectable object and then write an extremely simple client and time receiver.
It should be noted that MFC implements the iconnectionpointcontainer and ienumconnectionpoints interfaces through the ccmdtarget class, and implements the iconnectionpoint interface through the cconnectionpoint class.
- Connobject
In this object, a general COM interface ieventserver is implemented. You can use
Method dosomething () to do some things, but the main thing is that the object will trigger the event here. Sampleconnpoint
Implement the connection point object.
- Write in guids. h:
// {EE888B01-EA9C-11d3-97B5-5254AB191930}
Static const IID clsid_connobject = // component ID
{0xee888b01, 0xea9c, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0xab, 0x19, 0x19, 0x30 }};
// {EE888B02-EA9C-11d3-97B5-5254AB191930}
Static const IID iid_ieventserver = // General COM interface. The method used by the customer
// Dosomething ()
{0xee888b02, 0xea9c, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0xab, 0x19, 0x19, 0x30 }};
/// {EE888B03-EA9C-11d3-97B5-5254AB191930}
Static const IID iid_ieventsink = // ID of the connection point interface implemented by the connection point object
{0xee888b03, 0xea9c, 0x11d3, {0x97, 0xb5, 0x52, 0x54, 0xab, 0x19, 0x19, 0x30 }};
2. Write in iconnobject. h
# Include "guids. H"
// Declare the ieventserver Interface
Declare_interface _ (ieventserver, iunknown)
{
Stdmethod (dosomething) () pure;
};
// Declare the interface, which will be implemented by the customer's event Receiver
Declare_interface _ (ieventsink, iunknown)
{
Stdmethod (eventhandle) () pure;
};
3. Add the cconnobject class whose base class is c1_target. Add # include "iconnobject. h" to the class declaration file cconnobject. h and write it in the class declaration:
Protected:
......
// Declare the nested class that implements the ieventserver Interface
Begin_interface_part (eventserver, ieventserver)
Stdmethod (dosomething )();
End_interface_part (eventserver)
Declare_interface_map ()
// Declare Nested classes that implement connection points
Begin_connection_part (cconnobject, sampleconnpoint)
Connection_iid (iid_ieventsink)
End_connection_part (sampleconnpoint)
Declare_connection_map ()
Declare_olecreate (cconnobject)
In_connection_part and end_connection_part macro declare the nested class sampleconnpoint for the connection point, which is based on the cconnectionpoint class. If you need to overload the member function of the cconnectionpoint class or add your own member function, you can declare in these two macros. here, the connection_iid macro reloads the cconnectionpoint: getiid () function. use the DECLARE_CONNECTION-MAP () macro to declare the connector ing table.
4. Write in the implementation file of class cconnobject
Implement_olecreate (cconnobject, "connobject ",
0xee888b01, 0xea9c, 0x11d3, 0x97, 0xb5, 0x52, 0x54, 0xab, 0x19, 0x19, 0x30 );
Begin_interface_map (cconnobject, c1_target)
Interface_part (cconnobject, iid_ieventserver, eventserver)
Interface_part (cconnobject, iid_iconnectionpointcontainer, connptcontainer)
End_interface_map ()
Begin_connection_map (cconnobject, c1_target)
Connection_part (cconnobject, iid_ieventsink, sampleconnpoint)
End_connection_map ()
Note:. interface_part (cconnobject, iid_iconnectionpointcontainer, connptcontainer) must be written in the interface ing to implement the iconnectionpointpointiner interface. note that only the connptcontainer class is embedded in the ccmdtarget class to implement the iconnectionpointcontainer interface and use m_xconnptcontainer to record it.
B. begin_connection_map and end_connection_map macro are used to map the current connection points. connection_part defines the class to implement the connection points.
5. Write in cconnobject: cconnobject:
Enableconnections ();
6. Implement the ieventserver Interface
The ieventserver interface is based on the iunknown interface. The method for implementing the iunknown interface is not described here. Write in the implementation file:
Stdmethodimp
Cconnobject: xeventserver: dosomething ()
{
// Dosomething
Method_prologue (cconnobject, eventserver)
Pthis-> fireevent ();
Return s_ OK;
}
The dosomething () method can provide customers with the services they need. here we focus on the events where the connected object triggers the client event receiver. The fireevent () function is a special event trigger function implemented by the connobject class. The Code is as follows:
Void cconnobject: fireevent ()
{
// Obtain the connection pointer queue on the connection point
Const cptrarray * pconnections = m_xsampleconnpoint.getconnections ();
Assert (pconnections! = NULL );
Int cconnections = pconnections-> getsize ();
Ieventsink * pieventsink;
// Trigger an event for each connection
For (INT I = 0; I <cconnections; I ++)
{
// Obtain the client event receiver interface pointer
Pieventsink = (ieventsink *) (pconnections-> getat (I ));
Assert (pieventsink! = NULL );
// Call the event processing function of the client event Receiver
// This function is defined by the customer event receiver.
Pieventsink-> eventhandle ();
}
}
- Customer event receiver (sink)
The event receiver is also a COM Object and can also be implemented using nested classes, but it is only an internal
So there can be no CLSID and class factory. the following example shows a dialog box program with three buttons: "Connect" (idc_connect), "Disconnect" (idc_disconnect), and "Event" (idc_event ).
- Create a project based on the dialog box: connclient.
- In cconnclientdlg, first add # include "iconnobject. H", and then declare the event receiver nested class in the dialog box class declaration:
Begin_interface_part (eventsink, ieventsink)
Stdmethod (eventhandle )();
End_interface_part (eventsink)
Declare several private variables at the same time:
PRIVATE:
Lpconnectionpointcontainer pconnptcont; // record the Component Object
// Iconnectionpointcontainer interface pointer
Lpconnectionpoint pconnp; // record the connection point interface pointer
DWORD m_dwcookie; // record the connection ID
Iunknown * m_piunknown; // used to record the iunknown interface pointer of the Component Object
- Implement the event receiver:
Stdmethodimp _ (ulong)
Cconnclientdlg: xeventsink: addref ()
{
Return 1;
}
Stdmethodimp _ (ulong)
Cconnclientdlg: xeventsink: release ()
{
Return 0;
}
Stdmethodimp
Cconnclientdlg: xeventsink: QueryInterface (refiid riid, void ** ppvobj)
{
Method_prologue (cconnclientdlg, eventsink)
If (isequaliid (riid, iid_iunknown) |
Isequaliid (riid, iid_ieventsink ))
{
* Ppvobj = this;
Addref ();
Return s_ OK;
}
Else
{
Return e_nointerface;
}
}
Stdmethodimp
Cconnclientdlg: xeventsink: eventhandle () // this function will be called by a connectable object.
{
: Afxmessagebox ("the source object sends a notification to the event receiver! ");
Return s_ OK;
}
- Initialize the com library and create a component object instance
Write in cconnclientdlg: oninitdialog:
Hresult;
Hresult =: coinitialize (null );
If (failed (hresult ))
{
: Afxmessagebox ("the com library cannot be initialized! ");
Return false;
}
M_piunknown = NULL;
Hresult =: cocreateinstance (clsid_connobject, null,
Clsctx_inproc_server, iid_iunknown, (void **) & m_piunknown );
If (failed (hresult ))
{
M_piunknown = NULL;
: Afxmessagebox ("The connobject object cannot be created! ");
Return false;
}
M_dwcookie = 0; // the preset connection ID is 0.
- In the click event processing function void cconnclientdlg: onconnect () of the "Connect" (idc_connect) button, write:
Void cconnclientdlg: onconnect ()
{
If (m_dwcookie! = 0)
{
Return;
}
If (m_piunknown! = NULL)
{
Hresult;
Hresult = m_piunknown-> QueryInterface (iid_iconnectionpointcontainer,
(Void **) & pconnptcont );
If (failed (hresult ))
{
: Afxmessagebox ("The iconnectionpointcontainer interface of the object cannot be obtained! ");
Return;
}
Assert (pconnptcont! = NULL );
Hresult = pconnptcont-> findconnectionpoint (iid_ieventsink, & pcontreaty );
If (failed (hresult ))
{
Pconnptcont-> release ();
: Afxmessagebox ("The ieventsink connection point interface of the object cannot be obtained! ");
Return;
}
Assert (pconns! = NULL );
// Obtain the event receiver pointer
Iunknown * pieventsink;
M_xeventsink.queryinterface (iid_iunknown, (void **) & pieventsink );
// Send the event receiver pointer to the connectable object through the advise method of the connection point interface
If (succeeded (Fig-> advise (pieventsink, & m_dwcookie )))
{
: Afxmessagebox ("the connection to the connobject object can be established successfully! ");
}
Else
{
: Afxmessagebox ("cannot be connected to connobject! ");
}
Pconnp-> release ();
Pconnptcont-> release ();
Return;
}
}
The above Code establishes a connection with the connection point of the connectable object.
- The click handler function of the write button "Disconnect" (idc_disconnect) is as follows:
Void cconnclientdlg: ondisconnect ()
{
If (m_dwcookie = 0)
{
Return;
}
Pconnp-> unadvise (m_dwcookie );
Pconnp-> release ();
Pconnptcont-> release ();
M_dwcookie = 0;
}
- Compile the click handler for the "event" (idc_event) button:
Void cconnclientdlg: onevent ()
{
If (m_piunknown! = NULL)
{
Ieventserver * pieventserver;
Hresult;
Hresult = m_piunknown-> QueryInterface (iid_ieventserver, (void **) & pieventserver );
If (failed (hresult ))
{
: Afxmessagebox ("The ieventserver interface cannot be obtained! ");
Return;
}
Pieventserver-> dosomething ();
}
}
Here, the customer calls the service dosomething () provided by the component, and as we can see earlier, the component object will trigger a processing by the client event receiver in this function (cconnclientdlg: xeventsink :: eventhandle () event.
- When exiting the application:
Void cconnclientdlg: oncancel ()
{
M_piunknown-> release ();
: Couninitialize ();
Cdialog: oncancel ();
}
After running the program, click "Connect" and then click "Event". The MessageBox will pop up and prompt "the source object has sent a notification to the event Receiver !".
- Summary
It is precisely because of the mechanism of connectable objects that enables bidirectional communication between customers and component objects, and enables the component objects to have an event mechanism. this technology, similar to "server push", is very important in distributed application systems.
The example in this article is implemented based on the iunknown interface. In fact, it is more convenient to use the automatic interface idispatch as the output interface. it should be noted that using ATL to write connectable objects is more concise. The msdn document provides an example.