home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / inole2 / chap24 / patron / events.cpp < prev    next >
C/C++ Source or Header  |  1995-05-03  |  20KB  |  788 lines

  1. /*
  2.  * EVENTS.CPP
  3.  * Patron Chapter 24
  4.  *
  5.  * Implementation of the Events dialog box, the CEventMap class,
  6.  * and the events IDispatch on a tenant control site.
  7.  *
  8.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  9.  *
  10.  * Kraig Brockschmidt, Microsoft
  11.  * Internet  :  kraigb@microsoft.com
  12.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  13.  */
  14.  
  15.  
  16. #include "patron.h"
  17.  
  18.  
  19. /*
  20.  * EventsDlgProc
  21.  *
  22.  * Purpose:
  23.  *  Dialog procedure for the dialog in which the user can assign
  24.  *  actions to events.
  25.  */
  26.  
  27. BOOL APIENTRY EventsDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam
  28.     , LPARAM lParam)
  29.     {
  30.     PCEventMap      pEM=NULL;
  31.     HWND            hList;
  32.     UINT            i, iEvent;
  33.  
  34.     COMMANDPARAMS(wID, wCode, hWndMsg);
  35.  
  36.    #ifdef WIN32
  37.     pEM=(PCEventMap)GetProp(hDlg, PROP_POINTER);
  38.    #else
  39.     WORD            w1, w2;
  40.  
  41.     w1=(WORD)GetProp(hDlg, PROP_SELECTOR);
  42.     w2=(WORD)GetProp(hDlg, PROP_OFFSET);
  43.  
  44.     pEM=(PCEventMap)MAKELP(w1, w2);
  45.    #endif
  46.  
  47.     switch (iMsg)
  48.         {
  49.         case WM_INITDIALOG:
  50.             pEM=(PCEventMap)lParam;
  51.  
  52.            #ifdef WIN32
  53.             SetProp(hDlg, PROP_POINTER, (HANDLE)pEM);
  54.            #else
  55.             SetProp(hDlg, PROP_SELECTOR, (HANDLE)SELECTOROF(pEM));
  56.             SetProp(hDlg, PROP_OFFSET,   (HANDLE)OFFSETOF(pEM));
  57.            #endif
  58.  
  59.             /*
  60.              * Fill the listbox with events and select the first
  61.              * one.  The selection will cause an LBN_SELCHANGE
  62.              * notification which will set the appropriate
  63.              * radiobutton for the action.
  64.              */
  65.  
  66.             hList=GetDlgItem(hDlg, IDC_EVENTLIST);
  67.  
  68.             for (i=0; i < pEM->m_cEvents; i++)
  69.                 {
  70.                 //Add the name of the event to the list
  71.                #ifdef WIN32ANSI
  72.                 char        szTemp[40];
  73.  
  74.                 WideCharToMultiByte(CP_ACP, 0
  75.                     , pEM->m_pEventMap[i].bstrName, -1, szTemp
  76.                     , 40, NULL, NULL);
  77.                 iEvent=(UINT)SendMessage(hList, LB_ADDSTRING, 0
  78.                     , (LONG)(LPSTR)szTemp);
  79.                #else
  80.                 iEvent=(UINT)SendMessage(hList, LB_ADDSTRING, 0
  81.                     , (LONG)(LPSTR)pEM->m_pEventMap[i].bstrName);
  82.                #endif
  83.  
  84.                 if (LB_ERR!=iEvent)
  85.                     {
  86.                     //Give that item a pointer to the map data
  87.                     SendMessage(hList, LB_SETITEMDATA, iEvent
  88.                         , (LONG)&pEM->m_pEventMap[i]);
  89.                     }
  90.                 }
  91.  
  92.             //Set the initial action for the first item
  93.             SendMessage(hList, LB_SETCURSEL, 0, 0L);
  94.             CheckAction(hDlg, hList);
  95.             return TRUE;
  96.  
  97.         case WM_COMMAND:
  98.             hList=GetDlgItem(hDlg, IDC_EVENTLIST);
  99.  
  100.             switch (wID)
  101.                 {
  102.                 case IDOK:
  103.                     EndDialog(hDlg, 1);
  104.                     break;
  105.  
  106.                 case ID_TEST:
  107.                     TestSelection(hList);
  108.                     break;
  109.  
  110.                 case IDC_EVENTLIST:
  111.                     //Update the radiobuttons
  112.                     if (LBN_SELCHANGE==wCode)
  113.                         CheckAction(hDlg, hWndMsg);
  114.  
  115.                     //Double-click, same as hitting Test button
  116.                     if (LBN_DBLCLK==wCode)
  117.                         TestSelection(GetDlgItem(hDlg, IDC_EVENTLIST));
  118.                     break;
  119.  
  120.                 case IDC_BEEPNONE:
  121.                 case IDC_BEEPDEFAULT:
  122.                 case IDC_BEEPEXCLAMATION:
  123.                 case IDC_BEEPASTERISK:
  124.                 case IDC_BEEPHAND:
  125.                 case IDC_BEEPQUESTION:
  126.                     UpdateAction(hList, wID);
  127.                     break;
  128.                 }
  129.             return TRUE;
  130.         }
  131.  
  132.     return FALSE;
  133.     }
  134.  
  135.  
  136.  
  137. /*
  138.  * CheckAction
  139.  *
  140.  * Purpose:
  141.  *  Sets the appropriate radiobutton for the current listbox
  142.  *  selection
  143.  *
  144.  * Parameters:
  145.  *  hDlg            HWND of the dialog.
  146.  *  hList           HWND of the event list.
  147.  *
  148.  * Return Value:
  149.  *  None
  150.  */
  151.  
  152. void CheckAction(HWND hDlg, HWND hList)
  153.     {
  154.     UINT        i, idControl;
  155.     PEVENTMAP   pMap;
  156.  
  157.     i=(UINT)SendMessage(hList, LB_GETCURSEL, 0, 0L);
  158.     pMap=(PEVENTMAP)SendMessage(hList, LB_GETITEMDATA, i, 0L);
  159.  
  160.     if (LB_ERR==(LONG)pMap)
  161.         return;
  162.  
  163.     //Map the action to the button
  164.     switch (pMap->iAction)
  165.         {
  166.         case ACTION_NONE:            idControl=IDC_BEEPNONE; break;
  167.         case ACTION_BEEPDEFAULT:     idControl=IDC_BEEPDEFAULT; break;
  168.         case ACTION_BEEPASTERISK:    idControl=IDC_BEEPASTERISK; break;
  169.         case ACTION_BEEPEXCLAMATION: idControl=IDC_BEEPEXCLAMATION; break;
  170.         case ACTION_BEEPHAND:        idControl=IDC_BEEPHAND; break;
  171.         case ACTION_BEEPQUESTION:    idControl=IDC_BEEPQUESTION; break;
  172.         default:                     idControl=IDC_BEEPNONE; break;
  173.         }
  174.  
  175.     CheckRadioButton(hDlg, IDC_BEEPNONE, IDC_BEEPQUESTION, idControl);
  176.     return;
  177.     }
  178.  
  179.  
  180.  
  181. /*
  182.  * UpdateAction
  183.  *
  184.  * Purpose:
  185.  *  Sets the appropriate action in the event map for the
  186.  *  selected radiobutton.
  187.  *
  188.  * Parameters:
  189.  *  hList           HWND of the event list.
  190.  *  idControl       UINT identifier of the selected action control
  191.  *
  192.  * Return Value:
  193.  *  None
  194.  */
  195.  
  196. void UpdateAction(HWND hList, UINT idControl)
  197.     {
  198.     UINT        i;
  199.     PEVENTMAP   pMap;
  200.  
  201.     i=(UINT)SendMessage(hList, LB_GETCURSEL, 0, 0L);
  202.     pMap=(PEVENTMAP)SendMessage(hList, LB_GETITEMDATA, i, 0L);
  203.  
  204.     if (LB_ERR==(LONG)pMap)
  205.         return;
  206.  
  207.     //Map the button to the action
  208.     switch (idControl)
  209.         {
  210.         case IDC_BEEPNONE:        pMap->iAction=ACTION_NONE; break;
  211.         case IDC_BEEPDEFAULT:     pMap->iAction=ACTION_BEEPDEFAULT; break;
  212.         case IDC_BEEPASTERISK:    pMap->iAction=ACTION_BEEPASTERISK; break;
  213.         case IDC_BEEPEXCLAMATION: pMap->iAction=ACTION_BEEPEXCLAMATION; break;
  214.         case IDC_BEEPHAND:        pMap->iAction=ACTION_BEEPHAND; break;
  215.         case IDC_BEEPQUESTION:    pMap->iAction=ACTION_BEEPQUESTION; break;
  216.         default:                  pMap->iAction=ACTION_NONE; break;
  217.         }
  218.  
  219.     return;
  220.     }
  221.  
  222.  
  223.  
  224.  
  225.  
  226. /*
  227.  * TestSelection
  228.  *
  229.  * Purpose:
  230.  *  Executes the action associated with the currently selected
  231.  *  event.
  232.  *
  233.  * Parameters:
  234.  *  hList           HWND of the event list.
  235.  *
  236.  * Return Value:
  237.  *  None
  238.  */
  239.  
  240. void TestSelection(HWND hList)
  241.     {
  242.     UINT        i;
  243.     PEVENTMAP   pMap;
  244.  
  245.     i=(UINT)SendMessage(hList, LB_GETCURSEL, 0, 0L);
  246.     pMap=(PEVENTMAP)SendMessage(hList, LB_GETITEMDATA, i, 0L);
  247.  
  248.     //Event values corresond to MessageBeep values.
  249.     if (LB_ERR!=(LONG)pMap)
  250.         MessageBeep(pMap->iAction);
  251.  
  252.     return;
  253.     }
  254.  
  255.  
  256.  
  257.  
  258. //CEventMap implementations
  259.  
  260.  
  261. /*
  262.  * CEventMap::CEventMap
  263.  * CEventMap::~CEventMap
  264.  *
  265.  * Parameters (Constructor):
  266.  *  pITypeInfo      LPTYPEINFO from which to read event names.
  267.  */
  268.  
  269. CEventMap::CEventMap(LPTYPEINFO pITypeInfo)
  270.     {
  271.     m_cEvents=0;
  272.     m_pITypeInfo=pITypeInfo;
  273.     
  274.     if (NULL!=m_pITypeInfo)
  275.         m_pITypeInfo->AddRef();
  276.  
  277.     m_pEventMap=NULL;
  278.     return;
  279.     }
  280.  
  281.  
  282.  
  283.  
  284. CEventMap::~CEventMap(void)
  285.     {
  286.     if (NULL!=m_pITypeInfo)
  287.         m_pITypeInfo->Release();
  288.  
  289.     if (NULL!=m_pEventMap)
  290.         {
  291.         UINT        i;
  292.  
  293.         //Be sure to clean up allocated BSTRs
  294.         for (i=0; i < m_cEvents; i++)
  295.             SysFreeString(m_pEventMap[i].bstrName);
  296.  
  297.         delete [] m_pEventMap;
  298.         }
  299.  
  300.     return;
  301.     }
  302.  
  303.  
  304.  
  305. /*
  306.  * CEventMap::Init
  307.  *
  308.  * Purpose:
  309.  *  Initializes the event map with any operation prone to failure.
  310.  *  If this function fails, the caller should delete this object
  311.  *  immediately as it is unusable.
  312.  *
  313.  * Parameters:
  314.  *  None
  315.  *
  316.  * Return Value:
  317.  *  BOOL            TRUE if initialization succeeded, FALSE otherwise
  318.  */
  319.  
  320. BOOL CEventMap::Init(void)
  321.     {
  322.     LPTYPEATTR      pTA;
  323.     UINT            i;
  324.  
  325.     if (NULL==m_pITypeInfo)
  326.         return FALSE;
  327.  
  328.     if (FAILED(m_pITypeInfo->GetTypeAttr(&pTA)))
  329.         return FALSE;
  330.  
  331.     m_cEvents=pTA->cFuncs;
  332.     m_pITypeInfo->ReleaseTypeAttr(pTA);
  333.  
  334.     m_pEventMap=new EVENTMAP[m_cEvents];
  335.  
  336.     if (NULL==m_pEventMap)
  337.         {
  338.         m_cEvents=0;
  339.         return FALSE;
  340.         }
  341.  
  342.     for (i=0; i < m_cEvents; i++)
  343.         {
  344.         LPFUNCDESC     pFD;
  345.  
  346.         m_pEventMap[i].id=0;
  347.         m_pEventMap[i].bstrName=NULL;
  348.         m_pEventMap[i].iAction=ACTION_NONE;
  349.  
  350.         /*
  351.          * The only piece of information we want from for each
  352.          * event is the function name using ITypeInfo::GetNames.
  353.          *
  354.          * A more sophisticated container will probably save
  355.          * more information about each event here (such as
  356.          * parameter names and so forth) or access it dynamically
  357.          * when the end user wants to write code for events.
  358.          */
  359.  
  360.         if (SUCCEEDED(m_pITypeInfo->GetFuncDesc(i, &pFD)))
  361.             {
  362.             UINT        cNames;
  363.             HRESULT     hr;
  364.  
  365.             /*
  366.              * Since we only want the function name, we ask
  367.              * ITypeInfo::GetNames for only one function and pass
  368.              * the address of our one BSTR to it.  If we wanted all
  369.              * the names from ITypeInfo, then we'd allocate an
  370.              * array of BSTRs with "new BSTR[pFD->cParams+1]"
  371.              * and pass pFD->cParams+1 to GetNames below instead
  372.              * of just 1.  In either case, GetNames allocates
  373.              * the string and stores the pointer to it in our
  374.              * variable.
  375.              */
  376.  
  377.             m_pEventMap[i].id=pFD->memid;
  378.  
  379.             hr=m_pITypeInfo->GetNames(pFD->memid
  380.                 , &m_pEventMap[i].bstrName, 1, &cNames);
  381.  
  382.             m_pITypeInfo->ReleaseFuncDesc(pFD);
  383.             }
  384.         }
  385.  
  386.     return TRUE;
  387.     }
  388.  
  389.  
  390.  
  391.  
  392. /*
  393.  * CEventMap::Set
  394.  *
  395.  * Purpose:
  396.  *  Sets the event mapping of a specific ID to a given action.
  397.  *  To clear an event, call this function with the ID and
  398.  *  ACTION_NONE.
  399.  *
  400.  * Parameters:
  401.  *  id              DISPID of the event ID.
  402.  *  iAction         EVENTACTION to assign.
  403.  *
  404.  * Return Value:
  405.  *  BOOL            TRUE if the assignment happened, FALSE otherwise.
  406.  */
  407.  
  408. BOOL CEventMap::Set(DISPID id, EVENTACTION iAction)
  409.     {
  410.     BOOL        fRet=FALSE;
  411.  
  412.     if (NULL!=m_pEventMap)
  413.         {
  414.         UINT        i;
  415.  
  416.         for (i=0; i < m_cEvents; i++)
  417.             {
  418.             if (m_pEventMap[i].id==id)
  419.                 {
  420.                 m_pEventMap[i].iAction=iAction;
  421.                 fRet=TRUE;
  422.                 }
  423.             }
  424.         }
  425.  
  426.     return fRet;
  427.     }
  428.  
  429.  
  430.  
  431.  
  432. /*
  433.  * CEventMap::Get
  434.  *
  435.  * Purpose:
  436.  *  Retrieves the event assignment for a given ID.
  437.  *
  438.  * Parameters:
  439.  *  id              DISPID of the event ID.
  440.  *
  441.  * Return Value:
  442.  *  EVENTACTION     The action assigned to this ID.  ACTION_NONE
  443.  *                  if the ID is invalid.
  444.  */
  445.  
  446. EVENTACTION CEventMap::Get(DISPID id)
  447.     {
  448.     EVENTACTION iAction=ACTION_NONE;
  449.  
  450.     if (NULL!=m_pEventMap)
  451.         {
  452.         UINT        i;
  453.  
  454.         //Scan the list looking for the event
  455.         for (i=0; i < m_cEvents; i++)
  456.             {
  457.             if (m_pEventMap[i].id==id)
  458.                 {
  459.                 iAction=m_pEventMap[i].iAction;
  460.                 break;
  461.                 }
  462.             }
  463.         }
  464.  
  465.     return iAction;
  466.     }
  467.  
  468.  
  469.  
  470.  
  471. /*
  472.  * CEventMap::Serialize
  473.  * CEventMap::Deserialize
  474.  *
  475.  * Purpose:
  476.  *  Writes or reads the mappings from DISPID to actions
  477.  *  into or from a stream.
  478.  *
  479.  * Parameters:
  480.  *  pIStream        LPSTREAM into which to write or from which to
  481.  *                  read.
  482.  *
  483.  * Return Value:
  484.  *  None
  485.  */
  486.  
  487. void CEventMap::Serialize(LPSTREAM pIStream)
  488.     {
  489.     EVENTMAP        emTemp;
  490.     ULONG           cbWrite=sizeof(DISPID)+sizeof(EVENTACTION);
  491.  
  492.     if (NULL==pIStream)
  493.         return;
  494.  
  495.     /*
  496.      * Loop through all the IDs and write the ID and the action
  497.      * mapping only.  We don't need the event name because that
  498.      * will be retrieved when the control is again loaded.
  499.      *
  500.      * Writing these pieces of info means writing the first
  501.      * so many bytes of each EVENTMAP structure, ignoring the
  502.      * BSTR of the name.
  503.      */
  504.  
  505.     if (NULL!=m_pEventMap)
  506.         {
  507.         UINT        i;
  508.  
  509.         for (i=0; i < m_cEvents; i++)
  510.             pIStream->Write(&m_pEventMap[i], cbWrite, NULL);
  511.         }
  512.  
  513.     /*
  514.      * Finish off by writing a terminating EVENTMAP structure
  515.      * where the action is ACTION_TAILING which only have
  516.      * meaning here.  The ID is ignored in this tail.
  517.      */
  518.  
  519.     emTemp.id=0;
  520.     emTemp.iAction=ACTION_TAILING;
  521.     pIStream->Write(&emTemp, cbWrite, NULL);
  522.  
  523.     return;
  524.     }
  525.  
  526.  
  527.  
  528. void CEventMap::Deserialize(LPSTREAM pIStream)
  529.     {
  530.     if (NULL==pIStream)
  531.         return;
  532.  
  533.     /*
  534.      * When reading back the event mappings we have to be
  535.      * careful:  the control's event set might have changed
  536.      * in the meantime so some events may no longer exist and
  537.      * there may be new events.  Therefore we read each mapping
  538.      * one at a time (until we hit the tailing map) and find
  539.      * the ID in the current memory event map.  When we find
  540.      * a match we update the action in memory.
  541.      */
  542.  
  543.     if (NULL==m_pEventMap)
  544.         return;
  545.  
  546.     while (TRUE)
  547.         {
  548.         ULONG       cbRead=sizeof(DISPID)+sizeof(EVENTACTION);
  549.         HRESULT     hr;
  550.         EVENTMAP    em;
  551.  
  552.         hr=pIStream->Read(&em, cbRead, NULL);
  553.  
  554.         //Failure to read means a stream problem, to abort
  555.         if (FAILED(hr))
  556.             break;
  557.  
  558.         //If we hit the tail, we're done
  559.         if (ACTION_TAILING==em.iAction)
  560.             break;
  561.  
  562.         //Assign the action to the ID, if it exists
  563.         Set(em.id, em.iAction);
  564.         }
  565.  
  566.     return;
  567.     }
  568.  
  569.  
  570.  
  571.  
  572.  
  573. //Events IDispatch
  574.  
  575. /*
  576.  * CDispatchEvents::CDispatchEvents
  577.  * CDispatchEvents::~CDispatchEvents
  578.  *
  579.  * Parameters (Constructor):
  580.  *  pTen            PCTenant of the tenant we're in.
  581.  */
  582.  
  583. CDispatchEvents::CDispatchEvents(PCTenant pTen)
  584.     {
  585.     m_cRef=0;
  586.     m_pTen=pTen;
  587.     return;
  588.     }
  589.  
  590. CDispatchEvents::~CDispatchEvents(void)
  591.     {
  592.     return;
  593.     }
  594.  
  595.  
  596.  
  597.  
  598. /*
  599.  * CDispatchEvents::QueryInterface
  600.  * CDispatchEvents::AddRef
  601.  * CDispatchEvents::Release
  602.  *
  603.  * Purpose:
  604.  *  IUnknown members for CDispatchEvents object.
  605.  */
  606.  
  607. STDMETHODIMP CDispatchEvents::QueryInterface(REFIID riid, PPVOID ppv)
  608.     {
  609.     *ppv=NULL;
  610.  
  611.     if (IID_IUnknown==riid || IID_IDispatch==riid
  612.         || m_pTen->m_iidEvents==riid)
  613.         *ppv=this;
  614.  
  615.     if (NULL!=*ppv)
  616.         {
  617.         ((LPUNKNOWN)*ppv)->AddRef();
  618.         return NOERROR;
  619.         }
  620.  
  621.     return ResultFromScode(E_NOINTERFACE);
  622.     }
  623.  
  624. STDMETHODIMP_(ULONG) CDispatchEvents::AddRef(void)
  625.     {
  626.     return ++m_cRef;
  627.     }
  628.  
  629. STDMETHODIMP_(ULONG) CDispatchEvents::Release(void)
  630.     {
  631.     if (0!=--m_cRef)
  632.         return m_cRef;
  633.  
  634.     delete this;
  635.     return 0;
  636.     }
  637.  
  638.  
  639.  
  640.  
  641.  
  642. /*
  643.  * CDispatchEvents::GetTypeInfoCount
  644.  * CDispatchEvents::GetTypeInfo
  645.  * CDispatchEvents::GetIDsOfNames
  646.  *
  647.  * Purpose:
  648.  *  These type-information functions are not implemented.  The
  649.  *  only caller of this interface is a control which is the source
  650.  *  of the type information itself.  A control will not have a
  651.  *  need to call these functions.
  652.  *
  653.  * Return Value:
  654.  *  HRESULT         E_NOTIMPL in all cases.
  655.  */
  656.  
  657. STDMETHODIMP CDispatchEvents::GetTypeInfoCount(UINT *pctInfo)
  658.     {
  659.     *pctInfo=NULL;
  660.     return ResultFromScode(E_NOTIMPL);
  661.     }
  662.  
  663. STDMETHODIMP CDispatchEvents::GetTypeInfo(UINT itinfo
  664.     , LCID lcid, ITypeInfo **pptInfo)
  665.     {
  666.     *pptInfo=NULL;
  667.     return ResultFromScode(E_NOTIMPL);
  668.     }
  669.  
  670. STDMETHODIMP CDispatchEvents::GetIDsOfNames(REFIID riid
  671.     , OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID)
  672.     {
  673.     *rgszNames=NULL;
  674.     *rgDispID=NULL;
  675.     return ResultFromScode(E_NOTIMPL);
  676.     }
  677.  
  678.  
  679.  
  680.  
  681. /*
  682.  * CDispatchEvents::Invoke
  683.  *
  684.  * Purpose:
  685.  *  Notifies the container of the event in the control.  In this
  686.  *  container we look in the event mapping for this particular
  687.  *  site and execute the appropriate action recorded in that
  688.  *  mapping.  If there is no event handler set up, then nothing
  689.  *  happens.
  690.  *
  691.  * Parameters:
  692.  *  dispIDMember    DISPID of the method or property of interest.
  693.  *  riid            REFIID reserved, must be NULL.
  694.  *  lcid            LCID of the locale.
  695.  *  wFlags          USHORT describing the context of the invocation.
  696.  *  pDispParams     DISPPARAMS * to the array of arguments.
  697.  *  pVarResult      VARIANT * in which to store the result.  Is
  698.  *                  NULL if the caller is not interested.
  699.  *  pExcepInfo      EXCEPINFO * to exception information.
  700.  *  puArgErr        UINT * in which to store the index of an
  701.  *                  invalid parameter if DISP_E_TYPEMISMATCH
  702.  *                  is returned.
  703.  *
  704.  * Return Value:
  705.  *  HRESULT         NOERROR or a general error code.
  706.  */
  707.  
  708.  
  709. STDMETHODIMP CDispatchEvents::Invoke(DISPID dispIDMember, REFIID riid
  710.     , LCID lcid, unsigned short wFlags, DISPPARAMS * pDispParams
  711.     , VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
  712.     {
  713.     HRESULT     hr;
  714.     VARIANT     varResult;
  715.     EVENTACTION iAction;
  716.     UINT        i;
  717.     PEVENTMAP  pEM;
  718.  
  719.     ODSlu("Events IDispatch called with ID=%lu", dispIDMember);
  720.  
  721.     if (IID_NULL!=riid)
  722.         return ResultFromScode(E_INVALIDARG);
  723.  
  724.     /*
  725.      * We ignore lcid in this function.  A multilingual application
  726.      * might use it to determine the meaning of certain parameters
  727.      * or perhaps as an indication of how to format data like
  728.      * time, date, and currency or any other language or locale-
  729.      * sensitive data.
  730.      */
  731.  
  732.     /*
  733.      * Variable handling:  we don't actually do anything with any
  734.      * of the variables from the control's events, so we don't have
  735.      * any VARIANTARG variables to initialize.
  736.      */
  737.  
  738.     /*
  739.      * We don't handle the return value of any events if
  740.      * events have them.  We should, however, initialize an
  741.      * empty return value just so it's not garbage.
  742.      */
  743.     if(NULL==pVarResult)
  744.       pVarResult=&varResult;
  745.  
  746.     VariantInit(pVarResult);
  747.     V_VT(pVarResult)=VT_EMPTY;
  748.  
  749.  
  750.     //Only method calls are valid.
  751.     if (!(DISPATCH_METHOD & wFlags))
  752.         return ResultFromScode(DISP_E_MEMBERNOTFOUND);
  753.  
  754.     /*
  755.      * Process the event by looking for dispIDMember in the
  756.      * list maintained in the tenant that maps event IDs to
  757.      * actions.  If we find the ID, then we execute the action,
  758.      * otherwise we do nothing.
  759.      *
  760.      * Control containers that allow more sophisticated programming
  761.      * for events would do something on the same order but process
  762.      * parameters and call user-implemented functions instead of
  763.      * something simple like MessageBeep.
  764.      */
  765.  
  766.     iAction=ACTION_NONE;
  767.     pEM=m_pTen->m_pEventMap->m_pEventMap;
  768.  
  769.     for (i=0; i < m_pTen->m_pEventMap->m_cEvents; i++)
  770.         {
  771.         if (dispIDMember==pEM[i].id)
  772.             {
  773.             iAction=pEM[i].iAction;
  774.             break;
  775.             }
  776.         }
  777.  
  778.     if (ACTION_NONE==iAction)
  779.         hr=ResultFromScode(DISP_E_MEMBERNOTFOUND);
  780.     else
  781.         {
  782.         MessageBeep((UINT)iAction);
  783.         hr=NOERROR;
  784.         }
  785.  
  786.     return hr;
  787.     }
  788.