Creating a Message Sink

To handle messages and events, the Forms Manager delivers a control’s event and message data through a sink attached to an object when the application initializes. Many applications create an event sink as part of the initialization process. An event sink is an ActiveX object that exposes methods called by an OS to notify an application that an event has occurred. When an application thread calls DispatchMessage, the Forms Manager processes the message, and then routes the message through the appropriate application sink to the target control.

Microsoft® Windows® CE for the Auto PC contains two sink interfaces: IASEventSink and IASClassMsgSink. The IASEventSink interface delivers control commands, such as a WM_COMMAND message. The IASClassMsgSink interface delivers status messages, such as WM_ACTIVATE. A message sink must be placed on any object in a Forms Manager that requires a message. When you add a control to a form, the control inherits the form’s IASEventSink interface.

The Forms Manager first calls an application sink, then a control sink, and finally a form sink. By calling sinks in this order, the Forms Manager enables you to make controls into a subclass and override some OS default behavior. For example, if you must handle a prolonged keystroke at the application level, but you want to enable a short keystroke to pass to the control, you can process the keydown message at the application sink level.

An application implements one or more event or notification sinks to handle most messages. The following table shows the sinks that an application can implement.

Sink
Description
Primary event sink Handles most messages. It performs the same tasks that a WndProc function performs in a Windows-based application. Most applications require a primary event sink.
Optional class message sink Provides a way to receive messages, such as keystroke notifications, that are not sent to the primary event sink.

    To create an event sink class

  1. Create a class inheriting from IASEventSink.
  2. Implement COM methods in the IUnknown interface.
  3. Create a token implementation of IDispatch. IDispatch methods are required for compilation.
  4. Implement IASEventSink::ReceiveMsg for application-specific message handling.

The following code example shows how to create an event sink class.

class CAppEventSink : public IASEventSink
{
public:

  CAppEventSink() { m_cRef = 0; }        // Constructor initializes
                                         // reference count
    ~CAppEventSink(void) {}              // Destructor does nothing

    // IUnknown implementation
    // Standard COM QueryInterface. This application supports 
    // Iunknown, Idispatch, and IASEventSink.
   STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv)
      {
       if (!ppv) return E_INVALIDARG;  // Incoming pointer invalid

        // Return an interface pointer, if the requested interface is
        // implemented.
        if (riid == IID_ASEVENTSINK) *ppv = (PVOID) (IASEventSink *)
            this; 
        else if (riid == IID_IUnknown) *ppv = (PVOID) (IUnknown *) this;
        else if (riid == IID_IDispatch) *ppv = (PVOID) (IDispatch *)
            this;
        else return E_NOINTERFACE;

        AddRef(); //Add to reference count.

        return NOERROR;
     }

    // Increment the object reference count.
    STDMETHODIMP_(ULONG) AddRef(void)
    {
     InterlockedIncrement(&m_cRef);
     return m_cRef;         
    }

   // Decrease reference count. Delete this object, if necessary.
   STDMETHODIMP_(ULONG) Release(void)
   {
    if (InterlockedDecrement(&m_cRef) > 0)
    return m_cRef;

    delete this;
    return 0; 
   }            

 // IASEventSink
 // This method is invoked when an event is fired.
 // In this case, the application exits when it receives
 // the ID_EXIT command.
 STDMETHODIMP ReceiveMsg(long uMsg, long wParam, long lParam)
 {
     if(uMsg != WM_COMMAND) return S_FALSE; // Only WM_COMMAND
           // messages are processed.
  
  switch(HIWORD(lParam))
  {
   case ID_EXIT:
    PostThreadMessage(g_idThread, WM_QUIT, 0, 0);// Send the quit
                                                 // message to this
                                                 // application
                                                 // thread.
break;
     
   default:
    break;
  }
  return S_FALSE;
 }

 // IDispatch token implementation. All functions return E_NOTIMPL.
 STDMETHODIMP GetTypeInfoCount(UINT *pctInfo)   
  { return E_NOTIMPL; }           

 STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo
    **pptinfo)   
  { return E_NOTIMPL; }           

 STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT
       cNames, LCID lcid, DISPID *rgdispids)
  { return E_NOTIMPL; }           

 STDMETHODIMP Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD
      wFlags, DISPPARAMS *pdispparams,   
      VARIANT *pVarResult, EXCEPINFO *pei, UINT
      *puArgErr)      
  { return E_NOTIMPL; }           

protected:
    long  m_cRef; // This object's reference count
};

    To create an event sink object

  1. Use the new operator to create an event sink object.
  2. Call QueryInterface to get an interface.

The following code example shows how to create an event sink object.

HRESULT CreateEventSink(void)
{
    HRESULT hr;
    CAppEventSink *pSink;

    pSink = new CAppEventSink();  // Create an event sink object.
    if (!pSink) return E_OUTOFMEMORY;
    
    // Get an interface to keep
    hr = pSink->QueryInterface(IID_ASEVENTSINK, (PVOID *) &g_pSink);
    if (FAILED(hr)) delete pSink;
    
    return hr;
}