English

How To: Create event sink for ArcObjects

Summary

Instructions provided describe how to implement an event sink object for ArcObjects using ATL IDispEventSimpleImpl template interface with Microsoft Visual C++ 6.0.

Event sink is the process by which events raised in a COM object are handled by callback functions on a special interface of another object. Fundamentally, both Visual C++ and Visual Basic use event sinks to monitor COM object's state and respond to the predefined events generated by them. Both of the above programming environments have complete and practical events handling mechanisms for COM objects and especially for ActiveX controls. Custom implementations of event sink are occasionally required for a particular need.

Procedure

  1. Create a new MFC AppWizard project called EventSink. At Step 1 of the wizard, select Dialog based as the application type, accept all the default settings of the remainder of the wizard and click on Finish to create the project.
  2. Right-click on the EventSink project in the project explorer and select New ATL Object¡K in the context menu to add ATL support to the MFC project. No COM objects or controls must be added to the project, only ATL support is required.
  3. Add an ESRI MapControl to the project main dialog box IDD_EVENTSINK_DIALOG.
  4. Add a new class named CMCEventSinkObj. In the header file of CMCEventSinkObj class, add the following at the top,

    Code:
    #include "resource.h"
    #pragma warning(push)
    #pragma warning(disable : 4146)
    #pragma warning(disable : 4192)
    #import "C:\Program Files\arcgis\arcexe83\bin\esriCore.olb" raw_interfaces_only, raw_native_types, named_guids, exclude("OLE_COLOR", "OLE_HANDLE")
    #import "C:\Program Files\arcgis\arcexe83\bin\MapControl.ocx" no_namespace, named_guids
    #pragma warning(pop)


    Note:
    Import esriCore.olb before importing MapControl.ocx because MapControl.ocx relies on the types defined in the esriCore.olb.

  5. Derive the CMCEventSinkObj from IDispEventSimpleImpl<¡K> class template as following:

    Code:
    class CMCEventSinkObj : public IDispEventSimpleImpl<IDC_MAPCONTROL1, CMCEventSinkObj, &DIID_IMapControlEvents2>

    IDC_ MAPCONTROL1 is the ID of the map control, CMCEventSinkObj is the name of this event sink class, and DIID_IMapControlEvents2 is the GUID of the map control¡¦s event interface. In COM, events are actually outbound methods defined on a special interface. When events are fired, these outbound methods invoke the corresponding handlers defined in the event sink to process the events. So events in COM are nothing more than a group of methods in a special interface.
  6. Open the OLE/COM Object Viewer, or OLE View by name, to study the Map Control event interface. Select View TypeLib and open the Map Control (MapControl.ocx) located under C:\arcgis\arcexe83\bin\. Expand Dispinterface and locate IMapControlEvents2. Define a static object of type _ATL_FUNC_INFO about the event method to respond to. In this example, select to catch the onMouseDown event. In OLE View, OnMouseDown takes 6 parameters, 4 or type long and 2 of type double. So the declaration is the following:

    Code:
    static _ATL_FUNC_INFO MCOnMouseDownInfo = {CC_STDCALL, VT_I4, 6, {VT_I4, VT_I4, VT_I4, VT_I4, VT_R8, VT_R8}};

    Refer to the MSDN for more information about _ATL_FUNC_INFO.
  7. Define a member variable and a class constructor as following in class CMCEventSinkObj:

    Code:
    HWND m_hWndList;
    CMCEventSinkObj(HWND hWnd = NULL) : m_hWndList(hWnd)
    {
    }

  8. Define the event sink map as following in class CMCEventSinkObj:

    Code:
    BEGIN_SINK_MAP(CMCEventSinkObj)
    SINK_ENTRY_INFO(IDC_MAPCONTROL1, DIID_IMapControlEvents2, 1, MCOnMouseDown, &MCOnMouseDownInfo)
    END_SINK_MAP()

    Because the id of method onMouseDown in IMapControlEvents2 is 1, in decimal value, specify 1 as the entry id for this method. MCOnMouseDown is the event handler that would be invoked on this event.
  9. Define the MCOnMouseDown method as following in class CMCEventSinkObj:

    Code:
    HRESULT __stdcall MCOnMouseDown(short iButton, short iShift, long iX, long iY, double dMapX, double dMapY)
    {
    TCHAR buf[1024];
    sprintf(buf, "You clicked at point (%i, %i).", iX, iY);
    ::SendMessage(m_hWndList, LB_ADDSTRING,0, (LPARAM)buf);
    return S_OK;
    }

    This event handler obtains the point at which the user clicked the mouse on the map control and sends a message string 'You clicked at point (X, Y)' to a listbox in which this string will be displayed.

    Note:
    The parameter signature corresponds to the COM interface definition.

  10. The event sink object for the map control is finished. The following is the code to test this sink object. Add a listbox to the dialog and name it as IDC_LIST1. Uncheck Sort on the Style tab in the property page.
  11. Define a CMCEventSinkObj in CEventSinkDlg class:

    Code:
    CMCEventSinkObj* m_pMCEventSink;

  12. In the CEventSinkDlg::OnInitDialog() method, add the following at the bottom:

    Code:
    m_pMCEventSink = new CMCEventSinkObj((GetDlgItem(IDC_LIST1))->m_hWnd );
    HRESULT hr = m_pMCEventSink->DispEventAdvise((GetDlgItem(IDC_MAPCONTROL1)->GetControlUnknown()));

    Run the program. The listbox displays the message sent from the event sink object.

Related Information