This article first discusses the principles of connectivity objects and connection point mechanisms, and then explains how to use MFC to implement connectors and embedded event receivers for connectable objects and embedded customers. 1, the basic principle of connectivity objects and connection point mechanisms in order to Component objects and customers provide greater interaction capabilities, and component objects also need to actively communicate with customers. Component objects communicate with customers via an outgoing interface. If a component object defines one or more outgoing interfaces, this component object is called a connectable point object. The so-called exit is also a COM interface. Each outgoing interface contains a set of member 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 the interface is called. The event receiver is also a COM object. Connectable objects must implement an iconnectionPointContainer interface to manage all outlines. Each outgoing interface corresponds to a connection point object, the connection point object implements an iconnectionPoint interface. Customers are establishing a connection with the connectable object through the iconnectionPoint interface. Each connection is described with a ConnectData structure. ConnectData contains two members: IUNKNOWN * PUNK and DWORD DWCOOKIE. The PUNK corresponds to the iUnknown interface pointer to the client's event receiver; DWCookie is a 32-bit integer generated by the connection point object to uniquely identify this connection. With an enumerator interface IENUMCONNECTIONPOINTS implemented by a connectable object, customers can access all connection points for the connected object. But to get the IEnumConnectionPoints interface pointer, you have to pass the IconnectionPointContainer :: EnumConnectionPoints (IEnumConnectionPoints **) function, this function returns the enumerator interface pointer. Through another enumerator interface IEnumConnections implemented with an accessible object, all connections on a connection point cannot be accessed regardless of customer or connection objects. The IenumConnections interface pointer can be obtained through the IconnectionPoint :: EnumConnections function. In summary, a connectable object must implement four interfaces: IconnectionPointContainer, IconnectionPoint, IEnumConnectionPoints, IEnumConnections. For the definition of these four interfaces, please read the MSDN document. The process of connecting to objects and customer communication now is now briefly described. In the following example, the connectable object Connobject defines the interface IEventSink, and the interface is corresponding to this interface, which implements a connection point object SampleConnpoint (this object implements the connection point interface IconnectionPoint corresponding to the interface, the interface ID is IID_IEventsink). 1. After acquiring a client pointer m_pIUnknown IUnknown interface can be connected to the object, call m_pIUnknown-> QueryInterface (IID_IConnectionPointContainer, (void **) & pConnPtCont); If the call succeeds, IConnectionPointContainer interface pointer stored in the pConnPtCont connectable object. If the call is unsuccessful, it indicates that the object is not a connectable object. 2. Call PConnptCont-> FindConnectionPoint (IID_IEventsink, & PConnpt).
If the call is successful, PConnpt will store the connection point interface IconnectionPoint pointer implemented corresponding to the connection point object Sample Connection Objects IeventSink; if the call is not successful, it means that the connectable object does not support the interface IeventSink. 3. Call PConnpt-> Advise (PIEventSink, & M_DWCookie) to establish a connection to the event receiver and connection point. The PIEventSink is a pointer to the client event receiver iUnknown interface. This pointer passes this function to the connectable object to connect to the client to initiate communication to the customer; m_dwcookie is a connection ID, this value is saved by the client, customer This value is also disconnected. 4. Connectable objects can call methods in the customer event receiver through the connection point. After the client is successfully established, the connection point has been saved in the connection point and can call PConnpt-> getConnections () to get. 5. Customer calls PConnpt-> Unadvise (m_dwcookie) to cancel the connection, and call the PConnpt-> Release () release the connection point object. 2. Programming Examples Now use MFC to implement a connectable object and write an extremely simple customer and time receiver. It should be noted that the MFC implements the IconnectionPointContainer and the IENUMCONNECTIONPOINTS interface through the CCMDTarget class. In addition, the IConnectionPoint interface is implemented via the CConnectionPoint class 1. Connecting object Connobject In this object, you can implement a general COM interface IeventServer, customers can use this interface. Method DOSMETHING () makes something, but the main thing is that the object will trigger an event here. SampleConnpoint implements connection point objects. (1) 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 same IID_IEventServer = // General COM interface, customer use this interface method // dosomething () {0xee888B02, 0xEA9C, 0x11D3, {0x97, 0xB5, 0x52, 0x54, 0xAb, 0x19, 0x19, 0x30}}; {EE888B03-EA9C-11D3-97B5-5254AB191930} Static const same {0xEE888B03, 0x97, 0x11D3, {0x97, 0x11d3, {0x97, 0x11d3, {0x97, 0x11d3, {0x97, 0x11d3, {0x97, 0x11, 0x52, 0x54, 0xAb, 0x19, 0x19, 0x30}};
2. Write in IconnObject.h
#include "guids.h" // Declare the IeventServer interface declare_interface_ (iEventServer, IUnknown) {stdmethod () () () () () (}; //), this interface will be implemented by the customer's event receiver Declare_Interface_ (IeventSink, IUnknown) ) {Stdmethod () pure;};
3. Add the base class ccmdtarget class cconnobject. Add a #include "iconnobject.h" in class declaration file CConnobject1.h, write in class declaration: protected: ... // Declare the nested class for IeventServer interface BEGIN_INTERFACE_PART (EventServer, IEventServer) STDMETHOD (DoSomething) (); END_INTERFACE_PART (EventServer) DECLARE_INTERFACE_MAP () // declare nested classes implement connection point BEGIN_CONNECTION_PART (CConnObject, SampleConnPoint) CONNECTION_IID (IID_IEventSink) END_CONNECTION_PART (SampleConnPoint) DECLARE_CONNECTION_MAP () DECLARE_OLECREATE (CConnObject )
Description: begin_connection_part and end_connection_part macro declares the nested class SampleConnpoint for the connection point, and 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 it in these two macro. Here Connection_iid macro heavy loads the cconnectionpoint :: getiid () function. Use the declare_connection-map () macro connection point mapping 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, CCmdTarget) INTERFACE_PART (CConnObject, IID_IEventServer, EventServer) INTERFACE_PART (CConnObject, IID_IConnectionPointContainer , ConnPtContainer) END_INTERFACE_MAP () BEGIN_CONNECTION_MAP (CConnObject, CCmdTarget) CONNECTION_PART (CConnObject, IID_IEventSink, SampleConnPoint) END_CONNECTION_MAP () Description:. A must be written INTERFACE_PART (CConnObject, IID_IConnectionPointContainer, ConnPtContainer) in the interface map to implement the interface IConnectionPointContainer note, CCmdTarget. The class is embedded in the ConnptContainer class to implement the IConnectionPointContainer interface, and records with M_XConnptContainer. B. Realize the connection point mapping with begin_connection_map and end_connection_map macro. Connection_part defines the class that implements the connection point.
5. Write in cconnobject :: cconnobject ():
EnableConnections ();
6. Implement IeventServer interface
The IeventServer interface is based on IUNKNOWN interface, and the method of implementing the iUnknown interface is not described here. Write in the implementation file:
STDMETHODIMP CCONNOBJECT :: XeventServer :: DOSMETHING () {// DOSMETHING () {// DOSMETHING METHOD_PROLOGUE (CCONNOBJECT, EventServer) Pthis-> FireEvent (); returnes S_OK;} DOSMETHING () method can provide customers with a need for customers. Here is a connectable object The event that triggers the client event receiver is triggered here. The FireEvent () function is a function of the Connobject class implementation, the code is as follows: void cconnobject :: FireEvent () {// Get a connection point on the connection point Const CPTRARRAY * pConnections = m_xSampleConnPoint.GetConnections (); ASSERT (pConnections = NULL!); int cConnections = pConnections-> GetSize (); IEventSink * pIEventSink; // for each connection trigger event for (int i = 0; i BEGIN_INTERFACE_PART (EventSink, IEventSink) STDMETHOD (EventHandle) (); END_INTERFACE_PART (EventSink) while variables declared several private: private: LPCONNECTIONPOINTCONTAINER pConnPtCont; // // IConnectionPointContainer recording component object interface pointer LPCONNECTIONPOINT pConnPt; // DWORD Connector interface pointer points m_dwcookie; // Record connection ID iUnknown * m_piunknown; // to record component object IUNKNOWN interface pointers 3. 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 {:: AFXMessageBox ("source object to the event receiver!"); Return s_ok;} 4. Initialize the COM library and create components Object instance Writes in cconnclientdlg :: OnIndialog (): HRESULT hResult; hResult = :: CoInitialize (NULL); if (FAILED (hResult)) {:: AfxMessageBox ( "not initialize the COM library!"); 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 ( "ConnObject not create objects!"); return FALSE;} m_dwCookie = 0; // presets Connection logo is 0 5. Writes in the Click event handler void cconnetdlg :: onConnect () in the button "Connection" (IDC_CONNECT): void CConnClientDlg :: OnConnect () {if (m_dwCookie = 0!) {return;} if (m_pIUnknown = NULL!) {HRESULT hResult; hResult = m_pIUnknown-> QueryInterface (IID_IConnectionPointContainer, (void **) & pConnPtCont); if (FAILED (hResult)) {:: AfxMessageBox ( "not acquired object IConnectionPointContainer Interface!"); return;}! ASSERT (pConnPtCont = NULL); hResult = pConnPtCont-> FindConnectionPoint (IID_IEventSink, & pConnPt); if (FAILED (hResult)) {PCONNPTCONT-> Release (); :: afxMessageBox ("IeventSink connection point interface!"); returnn;} assert (PConnpt! = null); // Get event receiver pointer iUnknown * pieventsink; m_xeventsink.Queryinterface (IID_IUNKNOWN, (Void **) & PIEventSink; // Pass the event receiver pointer to the connectivity object if (succeeded (pieventsink, & m_dwcookie)) {:: afxMessageBox (PIEventSink, & M_DWCOOKIE)) "Connecting successfully");} else {:: AfxMessageBox ("Cannot Connect with ConnObject!");} PConnpt-> Release (); pconnptcont-> release (); return;}} The code establishes a connection with the connection point of the connectable object. 6. Writing button "Disabled" (idc_disconnect) The Click handler is as follows: Void cconnclientdlg :: onDisconnect () {if (m_dwcookie == 0) {return;} PConnpt-> unadvise (m_dwcookie); PConnpt-> Release (); pconnptcont-> release (); m_dwcookie = 0;} 7. Write button "Event" Click Processing Function: void CConnClientDlg :: OnEvent () {if (m_pIUnknown = NULL!) {IEventServer * pIEventServer; HRESULT hResult; hResult = m_pIUnknown-> QueryInterface (IID_IEventServer, (void **) & pIEventServer); if (FAILED (hResult)) {:: AFXMessageBox ("You cannot get IeventServer interface!"); Return;} PIEventServer-> dosomething ();}} Here, the customer calls the service DOSMETHING () provided by the component, and as seen earlier, the component object will trigger an event that is handled by the Customer Event Receiver (Cconnclientdlg :: Xeventsink :: EventHandle ()) in this function. 8. When exiting the application: Void cconclientdlg :: oncancel () {m_piunkn-> release () ;:: couninitialize (); cdialog :: oncance ();} After running the program, first click "Connection", then click the "Event" button, will pop up MessageBox, and prompt "Notification of the source object to the event receiver!" summary It is because there is a mechanism for connectivity objects, and the two-way communication of the client and component object is realized, making component objects have an event mechanism. This technology similar to "Server Push" is in a distributed application system It is very important. The examples shown in this paper are interested in use based on the IUnknown interface, in fact, using the automated interface IDispatch is more convenient to interface. It is necessary to write that the connection to write the connection object is more concise, and there is an example in the MSDN document.