Client-Side Event Sink

[Previous] [Next]

We need to check out one final feature of ATL before closing the book on connection points: setting up an event sink on the client side. ATL implements an event sink that lets you create an implementation of IDispatch for receiving events on the client side.

As an example, the TestEventSink application included on the companion CD includes an event sink that watches the events coming from the ATL message traffic control example in Chapter 10. Figure 12-4 shows the dialog box.

click to view at full size.

Figure 12-4. Dialog box with event sink.

The class that implements the event sink is named IDispEventImpl. To use the ATL-supplied event sink, derive a class from IDispEventImpl, instantiate the class, and connect the class to the event source. You can mix the IDispEventImpl class into an ATL windowing class (by using multiple inheritance) or you can create the event sink as a stand-alone class. Listing 12-5 illustrates how to use the event sink class to add an event sink to a dialog box. In Listing 12-5, the event sink exists as a nested class within an ATL-based dialog class.

Listing 12-5. Event sink in an ATL dialog class.

 class CSinkTestDlg :      public CAxDialogImpl<CSinkTestDlg> {     struct CAtlMsgTrafficEventSink :         public IDispEventImpl<1,              CAtlMsgTrafficEventSink,             &DIID_ _IATLMsgTrafficCtlEvents,             &LIBID_ATLMSGTRAFFICLib, 1, 0>     {         CSinkTestDlg* m_pSinkTestDlg;         CAtlMsgTrafficEventSink(CSinkTestDlg* pSinkTestDlg = NULL)         {             m_pSinkTestDlg = pSinkTestDlg;         };         void _ _stdcall OnExceededThreshold(long NumMessages,              long CurrentThreshold)         {             MessageBeep(0);         }         void _ _stdcall OnNewInterval(long NumMessages)         {             if(m_pSinkTestDlg)             {                 TCHAR sz[128];                 wsprintf(sz,                      "Number of messages: %ld",                      NumMessages);                 ::SetWindowText(                     ::GetDlgItem(m_pSinkTestDlg->m_hWnd,                      IDC_MSGCOUNT),                      sz);             }         }         BEGIN_SINK_MAP(CAtlMsgTrafficEventSink)             SINK_ENTRY_EX(1,                  DIID_ _IATLMsgTrafficCtlEvents,                  1, OnExceededThreshold)             SINK_ENTRY_EX(1,                  DIID_ _IATLMsgTrafficCtlEvents,                  2, OnNewInterval)         END_SINK_MAP()     }; // End of nested class      CAtlMsgTrafficEventSink* m_pAtlMsgTrafficEventSink;     IUnknown* m_pControlUnk;     bool m_bIsSinked;     CAxWindow m_ATLMsgTrafficSite;      }; 

Notice that the sink class has methods for responding to the events the ATL message traffic control throws out. Unfortunately, no wizard support is available yet for reading a component's type information and manufacturing the callback interfaces. We had to define and implement OnExceededThreshold and OnNewInterval by hand. The event sink map correlates the callback functions with their DISPIDs (1 and 2, respectively). Also notice that the IDispEventImpl template takes the name of the callback interface (DIID_ _IATLMsgTrafficCtlEvents) and the name of the type library defining the callback interface (LIBID_ATLMSGTRAFFICLib). The event sink uses this information to load the control's type information. The sink class also includes a back pointer to the dialog (because the sink class updates the dialog box every time there's a new interval). Then the dialog class includes a pointer to the control's IUnknown pointer, a pointer to the sink, an ATL ActiveX window, and information about whether the dialog box is connected to the object.

The dialog box connects itself to the control during the WM_INITDIALOG handler. The dialog box first creates a CAxWindow to host the control and then acquires the control's IUnknown pointer. The dialog box also creates an instance of the sink. The dialog box holds on to the event sink and to the control's IUnknown pointer—it needs these to connect to the control, as you'll see in a moment. Finally, notice that the dialog box starts the control's message-monitoring graph. Listing 12-6 shows how to create the control and connect the event sink to it.

Listing 12-6. Creating the control and connecting the sink to the control.

 LRESULT CSinkTestDlg::OnInitDialog(UINT uMsg,      WPARAM wParam,      LPARAM lParam,      BOOL& bHandled) {     ::SetWindowText(GetDlgItem(IDC_MSGCOUNT),          "Not sinked");     ::EnableWindow(GetDlgItem(IDC_ADVISE), FALSE);     ::EnableWindow(GetDlgItem(IDC_UNADVISE), FALSE);     RECT rect; = 10;     rect.left = 10;     rect.right = 650;     rect.bottom = 200;     HRESULT hr;     m_ATLMsgTrafficSite.Create(m_hWnd, rect,         _TEXT("{C30CCA3E-A482-11D2-8038-6425D1000000}"),         WS_CHILD | WS_VISIBLE);     IATLMsgTrafficCtl* pATLMsgTrafficCtl = 0;     hr = m_ATLMsgTrafficSite.QueryControl(IID_IATLMsgTrafficCtl,          (void**)&pATLMsgTrafficCtl);     if(SUCCEEDED(hr))     {         hr = pATLMsgTrafficCtl->StartGraph();         pATLMsgTrafficCtl->QueryInterface(IID_IUnknown,             (void**)&m_pControlUnk);         m_pAtlMsgTrafficEventSink =              new CAtlMsgTrafficEventSink(this);         if(m_pAtlMsgTrafficEventSink)         {             ::EnableWindow(GetDlgItem(IDC_ADVISE), TRUE);         }         pATLMsgTrafficCtl->Release();     }     return 1;  // Let the system set the focus. } 

The only thing left to do is respond to the Advise and Unadvise buttons. The OnClickedAdvise function is called whenever the user presses the Advise button. The event sink class inherits a function named DispEventAdvise, which handles all the connection points for you (thank goodness!). Conversely, the OnClickedUnadvise function (called when the user presses the Unadvise button) disconnects the sink from the dialog box, at which point the dialog box stops receiving events. The following code shows how to connect and disconnect the dialog box and the advise sink:

 LRESULT CSinkTestDlg::OnClickedAdvise(WORD wNotifyCode,      WORD wID,      HWND hWndCtl,      BOOL& bHandled) {     HRESULT hr;     if(m_pAtlMsgTrafficEventSink) {         hr = m_pAtlMsgTrafficEventSink->DispEventAdvise(m_pControlUnk);         if(SUCCEEDED(hr))         {             m_bIsSinked = TRUE;             ::EnableWindow(GetDlgItem(IDC_ADVISE), FALSE);             ::EnableWindow(GetDlgItem(IDC_UNADVISE), TRUE);         }     }     return 0; } LRESULT CSinkTestDlg::OnClickedUnadvise(WORD wNotifyCode,      WORD wID,      HWND hWndCtl,      BOOL& bHandled) {     HRESULT hr;     Hr  = m_pAtlMsgTrafficEventSink->DispEventUnadvise(         m_pControlUnk);     if(SUCCEEDED(hr))     {         m_bIsSinked = FALSE;         ::EnableWindow(GetDlgItem(IDC_ADVISE), TRUE);         ::EnableWindow(GetDlgItem(IDC_UNADVISE), FALSE);         ::SetWindowText(GetDlgItem(IDC_MSGCOUNT),              "Not sinked");     }     return 0; } 

Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127 © 2008-2017.
If you may any questions please contact us: