home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / mapi / command.ext / cmdext.cpp next >
Encoding:
C/C++ Source or Header  |  1996-04-11  |  24.6 KB  |  916 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. //  File Name
  4. //      CMDEXT.CPP 
  5. //
  6. //  Description
  7. //      Implementation of interface objects
  8. //
  9. //      IExchExt interface methods:
  10. //          MyExchExt::QueryInterface()
  11. //          MyExchExt::AddRef()
  12. //          MyExchExt::Release()
  13. //
  14. //          MyExchExt::Install()
  15. //
  16. //      IExchExtCommands interface methods:
  17. //          MyExchExtCommands::QueryInterface()
  18. //          MyExchExtCommands::AddRef()
  19. //          MyExchExtCommands::Release()
  20. //
  21. //          MyExchExtCommands::InstallCommands()
  22. //          MyExchExtCommands::DoCommand()
  23. //          MyExchExtCommands::InitMenu()
  24. //          MyExchExtCommands::Help()
  25. //          MyExchExtCommands::QueryHelpText()
  26. //          MyExchExtCommands::QueryButtonInfo()
  27. //          MyExchExtCommands::ResetToolbar()
  28. //
  29. //          MyExchExtCommands::SetContext()
  30. //          MyExchExtCommands::GetCmdID()
  31. //
  32. //      IExchExtUserEvents interface methods:
  33. //          MyExchExtUserEvents::QueryInterface()
  34. //          MyExchExtUserEvents::AddRef()
  35. //          MyExchExtUserEvents::Release()
  36. //
  37. //          MyExchExtUserEvents::OnSelectionChange()
  38. //          MyExchExtUserEvents::OnObjectChange()
  39. //
  40. //          MyExchExtUserEvents::SetContext()
  41. //          MyExchExtUserEvents::SetIExchExt()
  42. //
  43. //  Author
  44. //      Gary Peluso
  45. //
  46. //  Revision: 1.00
  47. //
  48. // Written for Microsoft Windows Developer Support
  49. // Copyright (c) 1992-1995 Microsoft Corporation. All rights reserved.
  50. //
  51. #define INITGUID
  52. #define USES_IID_IExchExt
  53. #define USES_IID_IExchExtAdvancedCriteria
  54. #define USES_IID_IExchExtAttachedFileEvents
  55. #define USES_IID_IExchExtCommands
  56. #define USES_IID_IExchExtMessageEvents
  57. #define USES_IID_IExchExtPropertySheets
  58. #define USES_IID_IExchExtSessionEvents
  59. #define USES_IID_IExchExtUserEvents
  60. #define USES_IID_IMAPIFolder
  61.  
  62. #include "CMDEXT.H"
  63.  
  64. ///////////////////////////////////////////////////////////////////////////////
  65. // data
  66. static HINSTANCE ghInstDLL = NULL;  // instance handle of DLL
  67.  
  68. ///////////////////////////////////////////////////////////////////////////////
  69. //    FUNCTION: DLLMain()
  70. //
  71. //    Purpose
  72. //    Do initilization processesing
  73. //
  74. //    Return Value
  75. //    TRUE - DLL successfully loads and LoadLibrary will succeed.
  76. //    FALSE - will cause an Exchange error message saying it cannot locate
  77. //            the extension DLL.
  78. //
  79. //    Comments
  80. //    We only need to get a copy of the DLL's HINSTANCE
  81. //
  82. BOOL WINAPI DllMain(
  83.     HINSTANCE  hinstDLL,
  84.     DWORD  fdwReason,   
  85.     LPVOID  lpvReserved) 
  86. {
  87.  if (DLL_PROCESS_ATTACH == fdwReason)
  88.  {
  89.     ghInstDLL = hinstDLL;
  90.  }
  91.  return TRUE;
  92. }
  93.  
  94. ///////////////////////////////////////////////////////////////////////////////
  95. //    FUNCTION: ExchEntryPoint
  96. //
  97. //    Parameters - none
  98. //
  99. //    Purpose
  100. //    The entry point which Exchange calls.  
  101. //
  102. //    Return Value
  103. //    Pointer to Exchange Extension Object
  104. //
  105. //    Comments
  106. //    This is called for each context entry.  Create a new MyExchExt object
  107. //    every time so each context will get its own MyExchExt interface.
  108. //
  109. LPEXCHEXT CALLBACK ExchEntryPoint(void)
  110. {
  111.     return new MyExchExt;
  112. }
  113.  
  114.  
  115. ///////////////////////////////////////////////////////////////////////////////
  116. //    MyExchExt::MyExchExt()
  117. //
  118. //    Parameters - none
  119. //
  120. //    Purpose
  121. //    Constructor. Initialize members and create supporting interface objects
  122. //
  123. //    Comments
  124. //    Each context of Exchange gets its own set of interface objects.
  125. //    Furthermore, interface objects per context are kept track of by Exchange
  126. //    and the interface methods are called in the proper context.
  127. //    
  128. MyExchExt::MyExchExt () 
  129.   m_cRef = 1; 
  130.   m_context = 0;
  131.   
  132.   m_pExchExtCommands = new MyExchExtCommands;
  133.   m_pExchExtUserEvents = new MyExchExtUserEvents;
  134.  
  135.   // in MyExchExtUserEvents methods I need a reference to MyExchExt
  136.   m_pExchExtUserEvents->SetIExchExt(this);
  137.  
  138. }
  139.  
  140.  
  141. ///////////////////////////////////////////////////////////////////////////////
  142. //  IExchExt virtual member functions implementation
  143. //
  144.  
  145. ///////////////////////////////////////////////////////////////////////////////
  146. //    MyExchExt::QueryInterface()
  147. //
  148. //    Parameters
  149. //    riid   -- Interface ID.
  150. //    ppvObj -- address of interface object pointer.
  151. //
  152. //    Purpose
  153. //    Called by Exchage to request for interfaces
  154. //
  155. //    Return Value
  156. //    S_OK  -- interface is supported and returned in ppvObj pointer
  157. //    E_NOINTERFACE -- interface is not supported and ppvObj is NULL
  158. //
  159. //    Comments
  160. //    Exchange client calls QueryInterface for each object.  Only
  161. //    Need to support objects that apply to the extension.  QueryInterface
  162. //    is called onces for each IID for each context.  We support two
  163. //    contexts in this example so QueryInterface is called twice for
  164. //    each IID.
  165. //    
  166. STDMETHODIMP MyExchExt::QueryInterface(REFIID riid, LPVOID * ppvObj)
  167. {
  168.     HRESULT hr = S_OK;
  169.  
  170.     *ppvObj = NULL;
  171.  
  172.     if ( (IID_IUnknown == riid) || (IID_IExchExt == riid) )
  173.     {
  174.         *ppvObj = (LPUNKNOWN)this;
  175.     }
  176.     else if ( IID_IExchExtCommands == riid)
  177.     {
  178.         *ppvObj = (LPUNKNOWN)m_pExchExtCommands;
  179.         m_pExchExtCommands->SetContext(m_context);
  180.     }
  181.     else if ( IID_IExchExtUserEvents == riid)
  182.     {
  183.         *ppvObj = (LPUNKNOWN)m_pExchExtUserEvents;
  184.         m_pExchExtUserEvents->SetContext(m_context);
  185.     }
  186.     else
  187.         hr = E_NOINTERFACE;
  188.  
  189.     if (NULL != *ppvObj)
  190.         ((LPUNKNOWN)*ppvObj)->AddRef();
  191.  
  192.     return hr;
  193. }
  194.  
  195.  
  196.  
  197. ///////////////////////////////////////////////////////////////////////////////
  198. //    MyExchExt::Install()
  199. //
  200. //    Parameters
  201. //    peecb     -- pointer to Exchange Extension callback function
  202. //    eecontext -- context code at time of being called.
  203. //    ulFlags   -- flag to say if install is for modal or not
  204. //
  205. //    Purpose
  206. //    Called once for each new contexted that is entered.  Proper version 
  207. //    number is checked here.  
  208. //
  209. //    Return Value
  210. //    S_OK -- object supported in the requested context
  211. //    S_FALSE -- object is not supported in teh requested context
  212. //
  213. //    Comments
  214. //
  215.  STDMETHODIMP MyExchExt::Install (LPEXCHEXTCALLBACK pmecb, 
  216.                          ULONG mecontext, ULONG ulFlags)
  217. {
  218.     ULONG ulBuildVersion;
  219.     HRESULT hr;
  220.  
  221.     m_context = mecontext;
  222.  
  223.     // make sure this is the right version 
  224.     pmecb->GetVersion(&ulBuildVersion, EECBGV_GETBUILDVERSION);
  225.     if (EECBGV_BUILDVERSION_MAJOR != (ulBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK))
  226.         return S_FALSE;
  227.  
  228.     switch (mecontext)
  229.     {
  230.         case EECONTEXT_VIEWER:
  231.         case EECONTEXT_SEARCHVIEWER:
  232.             hr = S_OK;
  233.             break;
  234.         default:
  235.             hr = S_FALSE;
  236.             break;
  237.     }
  238.     return hr;
  239. }
  240.  
  241. ///////////////////////////////////////////////////////////////////////////////
  242. //  IExchExtCommands virtual member functions implementation
  243. //
  244.  
  245. ///////////////////////////////////////////////////////////////////////////////
  246. //    MyExchExtCommands::QueryInterface()
  247. //
  248. //    Parameters
  249. //    riid   -- Interface ID.
  250. //    ppvObj -- address of interface object pointer.
  251. //
  252. //    Purpose
  253. //    Exchange Client does not call IExchExtCommands::QueryInterface().  
  254. //    So return nothing.
  255. //
  256. //    Return Value - none
  257. //
  258.  
  259. STDMETHODIMP MyExchExtCommands::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)          
  260. {
  261.     *ppvObj = NULL;
  262.     if ( (riid == IID_IExchExtCommands) || (riid == IID_IUnknown) )
  263.     {
  264.         *ppvObj = (LPVOID)this;
  265.         // Increase usage count of this object
  266.         AddRef();
  267.         return S_OK;
  268.     }
  269.     return E_NOINTERFACE;
  270. }
  271.  
  272. ///////////////////////////////////////////////////////////////////////////////
  273. //    MyExchExtCommands::InstallCommands()
  274. //
  275. //    Parameters
  276. //    pmecb  -- Exchange Callback Interface
  277. //    hWnd   -- window handle to main window of context
  278. //    hMenu  -- menu handle to main menu of context
  279. //    lptbeArray -- array of toolbar button entries
  280. //    ctbe   -- count of button entries in array
  281. //    ulFlags -- reserved
  282. //
  283. //    Purpose
  284. //    This function is called when commands are installed for each context
  285. //    the extension services.
  286. //
  287. //    Return Value
  288. //    S_FALSE means the commands have been handled.
  289. //
  290. //    Comments
  291. //    The hWnd and hMenu are in context.  If the context is for the SENDNOTE 
  292. //    dialog, then the hWnd is the window handle to the dialog and the hMenu is
  293. //    the main menu of the dialog.
  294. //    
  295. //    Call ResetToolbar so that Exchange will show it's toolbar
  296. //
  297.  
  298.  
  299. STDMETHODIMP MyExchExtCommands::InstallCommands(LPEXCHEXTCALLBACK pmecb, 
  300.                             HWND hWnd, HMENU hMenu,
  301.                             UINT FAR * pcmdidBase, LPTBENTRY lptbeArray,
  302.                             UINT ctbe, ULONG ulFlags)
  303. {
  304.  HRESULT hr = S_FALSE;
  305.  HMENU hMenuTools;
  306.  
  307.  if ((EECONTEXT_SEARCHVIEWER == m_context) || (EECONTEXT_VIEWER == m_context))
  308.  {
  309.  
  310.  
  311.   // -------  install the new menu command, append to end of Tools menu. ----
  312.   m_hWnd = hWnd;
  313.  
  314.   pmecb->GetMenuPos(EECMDID_ToolsCustomizeToolbar, &hMenuTools, NULL, NULL, 0);
  315.  
  316.  
  317.   // add a menu separator
  318.    AppendMenu(hMenuTools, MF_SEPARATOR, 0, NULL);
  319.  
  320.   // add our extension command
  321.    AppendMenu(hMenuTools,
  322.          MF_BYPOSITION | MF_STRING, 
  323.          *pcmdidBase, 
  324.          "Folder Stats");
  325.  
  326.      m_cmdid = *pcmdidBase;
  327.  
  328.      (*pcmdidBase)++;
  329.  
  330.  }
  331.  
  332.  if (EECONTEXT_VIEWER == m_context)
  333.  {
  334.  
  335.   // --------------  install the new toolbar button --------------
  336.  
  337.   // walk through the toolbars and find the standard toolbar
  338.   
  339.   int tbindx;
  340.   HWND hwndToolbar = NULL;
  341.   for (tbindx = ctbe-1; (int) tbindx > -1; --tbindx)
  342.   {
  343.    if (EETBID_STANDARD == lptbeArray[tbindx].tbid)
  344.    {
  345.     hwndToolbar = lptbeArray[tbindx].hwnd;
  346.     m_itbb = lptbeArray[tbindx].itbbBase++;
  347.     break;
  348.    }
  349.   }
  350.  
  351.   // add out button's bitmap to the toolbar's set of buttons
  352.   if (hwndToolbar)
  353.   {
  354.    TBADDBITMAP tbab;
  355.  
  356.    tbab.hInst = ghInstDLL;
  357.    tbab.nID = IDB_EXTBTN;
  358.    m_itbm = SendMessage(hwndToolbar, TB_ADDBITMAP, 1, (LPARAM)&tbab);
  359.   }
  360.  
  361.  
  362.  }
  363.  
  364.  return hr;
  365.  
  366. }
  367.  
  368.  
  369. ///////////////////////////////////////////////////////////////////////////////
  370. //    MyExchExtCommands::DoCommand()
  371. //
  372. //    Parameters
  373. //    pmecb -- pointer to Exchange Callback Interface
  374. //
  375. //    Purpose
  376. //    This method is called by Exchange for each WM_COMMAND is sent to the
  377. //    window in context. 
  378. //
  379. //    Return Value
  380. //    S_OK if command is handled
  381. //    S_FALSE if command is not handled
  382. //
  383. //    Comments
  384. //    Use this function to either respond to the command item (menu or toolbar)
  385. //    added or modify an existing command in Exchange.  Return S_OK to let
  386. //    Exchange know the command was handled.  Return S_OK on commands you are
  387. //    taking over from Exchange.  Return S_FALSE to let Exchange know you want
  388. //    it to carry out its command, even if you modify its action.
  389. //
  390.                                                                                                           
  391. STDMETHODIMP MyExchExtCommands::DoCommand(LPEXCHEXTCALLBACK pmecb, UINT cmdid)
  392. {
  393.  static char szBuffer[200];
  394.  ULONG uNumSelected = 0;
  395.  ULONG ulSubFolders = 0;
  396.  ULONG ulReadMsgs = 0;
  397.  ULONG ulUnReadMsgs = 0;
  398.  LPMDB lpMDB = NULL;
  399.  LPMAPIFOLDER lpFolder = NULL;
  400.  LPMAPISESSION lpSession = NULL;
  401.  LPADRBOOK lpAddrBook = NULL;
  402.  ULONG cbeid;
  403.  LPENTRYID lpeid = NULL;
  404.  ULONG ulType;
  405.  char szMsgClass[50];
  406.  ULONG cbMsgClass;
  407. // ULONG ulMsgFlags; 
  408.  
  409.  HRESULT hr = S_FALSE;   // assume it is not our command
  410.  
  411.  if (m_cmdid != cmdid)
  412.     return hr;           // not our command
  413.  
  414.  if (m_context == EECONTEXT_VIEWER)
  415.  {
  416.  
  417.     cbMsgClass = 49;
  418.     szMsgClass[0] = '\0';
  419.  
  420.  
  421.     hr = pmecb->GetSession(&lpSession, &lpAddrBook);
  422.     if (FAILED(hr))
  423.     {
  424.         hr = S_OK;     // we still handled the command
  425.         goto error_return;
  426.     }
  427.  
  428.     hr = pmecb->GetSelectionItem(0, &cbeid, &lpeid, &ulType, NULL, 
  429.         0, NULL, 0);
  430.     if (FAILED(hr))
  431.     {
  432.         hr = S_OK;     // we still handled the command
  433.         goto error_return;
  434.     }
  435.  
  436.     if (ulType != MAPI_FOLDER)          // should never get this
  437.     {
  438.         hr = S_OK;     // we still handled the command
  439.         goto error_return;
  440.     }
  441.  
  442.  
  443.     hr = lpSession->OpenEntry(cbeid, lpeid, NULL, 0, &ulType, 
  444.                          (LPUNKNOWN FAR *)&lpFolder);
  445.     if (FAILED(hr))
  446.     {
  447.         hr = S_OK;     // we still handled the command
  448.         goto error_return;
  449.     }
  450.     
  451.  
  452.     if (NULL == lpFolder) 
  453.     {
  454.         hr = S_OK;     // we still handled the command
  455.         goto error_return;              // no folder, not going to continue
  456.     }
  457.  
  458.     GetFolderStats(lpFolder, &ulSubFolders, &ulReadMsgs, &ulUnReadMsgs);
  459.     wsprintf(szBuffer, "Number of subfolders: %ld\n"
  460.                        "Number of read messages: %ld\n"
  461.                        "Number of unread messages: %ld", 
  462.                        ulSubFolders, ulReadMsgs, ulUnReadMsgs);
  463.  
  464.     MessageBox(m_hWnd, szBuffer, "Sample Extension - Folder Stats", MB_OK);
  465.  
  466.     hr = S_OK;
  467.     
  468.  }
  469.  
  470.  // must use a different technique in Find window
  471.  if (m_context == EECONTEXT_SEARCHVIEWER)  
  472.  {
  473.  
  474.  
  475.     pmecb->GetObject(&lpMDB, (LPMAPIPROP FAR *)&lpFolder);
  476.  
  477.     if (lpFolder == NULL)
  478.     {
  479.         hr = S_OK;     // we still handled the command
  480.         goto error_return;              // no folder, not going to continue
  481.     }
  482.  
  483.     GetFolderStats(lpFolder, &ulSubFolders, &ulReadMsgs, &ulUnReadMsgs);
  484.     wsprintf(szBuffer, "Number of subfolders: %d\n"
  485.                        "Number of read messages: %d\n"
  486.                        "Number of unread messages: %d", 
  487.                        ulSubFolders, ulReadMsgs, ulUnReadMsgs);
  488.  
  489.  
  490.     MessageBox(m_hWnd, szBuffer, "Sample Extension - Folder Stats", MB_OK);
  491.  
  492.     hr = S_OK;
  493.  
  494.  }
  495.  
  496. error_return:
  497.  
  498.     MAPIFreeBuffer(lpeid);
  499.     
  500.     if (NULL != lpSession)
  501.         lpSession->Release();
  502.  
  503.     if (NULL != lpAddrBook)
  504.         lpAddrBook->Release();
  505.  
  506.     if (NULL != lpMDB)
  507.         lpMDB->Release();                        
  508.  
  509.     if (NULL != lpFolder)
  510.         lpFolder->Release();
  511.  
  512.  return hr; 
  513. }
  514.  
  515.  
  516. ///////////////////////////////////////////////////////////////////////////////
  517. //    MyExchExtCommands::InitMenu()
  518. //
  519. //    Parameters
  520. //    pmecb -- pointer to Exchange Callback Interface
  521. //
  522. //    Purpose
  523. //    This method is called by Exchange when the menu of context is about to
  524. //    be activated.  See WM_INITMENU in the Windows API Reference for more
  525. //    information.
  526. //
  527. //    Return Value - none
  528. //
  529.  
  530. STDMETHODIMP_(VOID) MyExchExtCommands::InitMenu(LPEXCHEXTCALLBACK pmecb)
  531. {
  532.  // do nothing
  533.  
  534. }
  535.  
  536. ///////////////////////////////////////////////////////////////////////////////
  537. //    MyExchExtCommands::Help()
  538. //
  539. //    Parameters
  540. //    pmecb -- pointer to Exchange Callback Interface
  541. //    cmdid -- command id
  542. //
  543. //    Purpose
  544. //    Respond when user presses F1 while custom menu item is selected.
  545. //
  546. //    Return Value
  547. //    S_OK -- recognized the command and provided help
  548. //    S_FALSE -- not our command and we didn't provide help
  549. //
  550.  
  551. STDMETHODIMP MyExchExtCommands::Help(LPEXCHEXTCALLBACK pmecb, UINT cmdid)
  552. {
  553.  HRESULT hr;
  554.  
  555.  
  556.  if (cmdid == m_cmdid)
  557.  {
  558.  
  559.    MessageBox(m_hWnd, "Called through IExchExtCommands::Help()\n"
  560.                       "Contained in cmdext32.dll\n\n"
  561.                       "Copyright (c) 1995 Microsoft Corporation.\n"
  562.                       "All rights reserved.",
  563.                       "About Folder Stats Sample Extension", MB_OK);
  564.  
  565.    hr = S_OK;
  566.  } 
  567.  else
  568.    hr = S_FALSE;
  569.  
  570.  return hr;
  571. }
  572.  
  573.  
  574. ///////////////////////////////////////////////////////////////////////////////
  575. //    MyExchExtCommands::QueryHelpText()
  576. //
  577. //    Parameters
  578. //    cmdid -- command id corresponding to menu item activated
  579. //    ulFlags -- identifies either EECQHT_STATUS or EECQHT_TOOLTIP
  580. //    psz -- pointer to buffer to be populated with text to display
  581. //    cch -- count of characters available in psz buffer
  582. //
  583. //    Purpose
  584. //    Exchange calls this function each time it requires to update the status
  585. //    bar text or if it is to display a tooltip on the toolbar.
  586. //
  587. //    Return Value
  588. //    S_OK to indicate our command was handled
  589. //    S_FALSE to tell Exchange it can continue with its function
  590. //
  591.  
  592. STDMETHODIMP MyExchExtCommands::QueryHelpText(UINT cmdid, ULONG ulFlags, 
  593.                                               LPTSTR psz, UINT cch)
  594. {
  595.  
  596.  HRESULT hr;
  597.  
  598.  if (cmdid == m_cmdid)
  599.  {
  600.   if (ulFlags == EECQHT_STATUS)
  601.     lstrcpyn(psz, "Display Stats on Current Folder", cch);
  602.   
  603.   if (ulFlags == EECQHT_TOOLTIP)
  604.     lstrcpyn(psz, "Folder Stats", cch);
  605.  
  606.   hr = S_OK;
  607.  
  608.  }
  609.  else
  610.   hr = S_FALSE;
  611.  
  612.  return hr;
  613. }
  614.  
  615. ///////////////////////////////////////////////////////////////////////////////
  616. //    MyExchExtCommands::QueryButtonInfo()
  617. //
  618. //    Parameters
  619. //    tbid    -- toolbar identifier
  620. //    itbb    -- toolbar button index
  621. //    ptbb    -- pointer to toolbar button structure -- see TBBUTTON structure
  622. //    lpsz    -- point to string describing button
  623. //    cch     -- maximum size of lpsz buffer
  624. //    ulFlags -- EXCHEXT_UNICODE may be specified
  625. //
  626. //    Purpose
  627. //    For Exchange to find out about toolbar button information.
  628. //
  629. //    Return Value
  630. //    S_FALSE - not our button
  631. //    S_OK    - we filled information about our button
  632. //
  633. //    Comments
  634. //    Called for every button installed for toolbars when IExchExtCommands
  635. //    is installed for each context. The lpsz text is used when the Customize
  636. //    Toolbar dialog is displayed.  The text will be displayed next to the
  637. //    button.
  638. //
  639.  
  640. STDMETHODIMP MyExchExtCommands::QueryButtonInfo (ULONG tbid, UINT itbb, 
  641.                             LPTBBUTTON ptbb, LPTSTR lpsz, UINT cch, 
  642.                             ULONG ulFlags)
  643. {
  644.  HRESULT hr = S_FALSE;
  645.  
  646.   if (m_itbb == itbb)
  647.   {
  648.    ptbb->iBitmap = m_itbm;             // see InstallCommands in this source file
  649.    ptbb->idCommand = m_cmdid;
  650.    ptbb->fsState = TBSTATE_ENABLED;
  651.    ptbb->fsStyle = TBSTYLE_BUTTON;
  652.    ptbb->dwData = 0;
  653.    ptbb->iString = -1;
  654.    lstrcpyn(lpsz, "Folder Stats Toolbar Button", cch);
  655.  
  656.    hr = S_OK;
  657.   }
  658.  return hr;
  659. }
  660.  
  661.  
  662. ///////////////////////////////////////////////////////////////////////////////
  663. //    MyExchExtCommands::ResetToolbar()
  664. //
  665. //    Parameters
  666. //    tbid
  667. //    ulFlags
  668. //
  669. //    Purpose
  670. //
  671. //    Return Value  S_OK always
  672. //
  673. STDMETHODIMP MyExchExtCommands::ResetToolbar(ULONG tbid, ULONG ulFlags)
  674. {
  675.  return S_OK;
  676. }
  677.  
  678. ///////////////////////////////////////////////////////////////////////////////
  679. //  IExchExtUserEvents virtual member functions implementation
  680. //
  681.  
  682. ///////////////////////////////////////////////////////////////////////////////
  683. //    MyExchExtUserEvents::QueryInterface()
  684. //
  685. //    Parameters
  686. //    riid   -- Interface ID.
  687. //    ppvObj -- address of interface object pointer.
  688. //
  689. //    Purpose
  690. //    Exchange Client does not call IExchExtUserEvents::QueryInterface().  
  691. //    So return nothing.
  692. //
  693. //    Return Value - none
  694. //
  695.  
  696. STDMETHODIMP MyExchExtUserEvents::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)          
  697. {
  698.     *ppvObj = NULL;
  699.     if (( riid == IID_IExchExtUserEvents) || (riid == IID_IUnknown) )
  700.     {
  701.         *ppvObj = (LPVOID)this;
  702.         // Increase usage count of this object
  703.         AddRef();
  704.         return S_OK;
  705.     }
  706.     return E_NOINTERFACE;
  707. }
  708.  
  709. ///////////////////////////////////////////////////////////////////////////////
  710. //    MyExchExtUserEvents::OnSelectionChange()
  711. //
  712. //    Parameters
  713. //    pmecb  -- pointer to Exchange Callback Object
  714. //    
  715. //
  716. //    Purpose
  717. //    This function is called when the selection in the UI is changed.
  718. //
  719. //    Return Value - none
  720. //
  721. //    Comments
  722. //    OnSelectionChange is called whenever the selection changes either within
  723. //    a pane or is changed between panes.
  724. //
  725.  
  726. STDMETHODIMP_(VOID) MyExchExtUserEvents::OnSelectionChange(LPEXCHEXTCALLBACK pmecb)
  727. {
  728.  static ULONG cbeid;
  729.  static LPENTRYID lpeid = NULL;
  730.  static ULONG ulType;
  731.  static char szMsgClass[50];
  732.  ULONG cbMsgClass;
  733.  ULONG ulMsgFlags;
  734.  HMENU hMenu;
  735.  HRESULT hr;
  736.  UINT cmdid;
  737.  MyExchExtCommands * pExchExtCommands = NULL;
  738.  
  739.  // do not care about enabling or disabling menu item if in the search viewer
  740.  if (m_context == EECONTEXT_SEARCHVIEWER)
  741.     return;
  742.  
  743.  cbMsgClass = 49;
  744.  szMsgClass[0] = '\0';
  745.  
  746.  
  747.  hr = pmecb->GetSelectionItem(0, &cbeid, &lpeid, &ulType, szMsgClass, 
  748.         cbMsgClass, &ulMsgFlags, 0);
  749.  
  750.  if (FAILED(hr))
  751.  {
  752.   goto error_return;
  753.  }
  754.  
  755.  
  756.  pmecb->GetMenu(&hMenu);
  757.  
  758.  hr = m_pExchExt->QueryInterface(IID_IExchExtCommands, (LPVOID *)&pExchExtCommands);
  759.  if (FAILED(hr))
  760.  {
  761.     goto error_return;
  762.  }
  763.  
  764.  cmdid = pExchExtCommands->GetCmdID();
  765.  pExchExtCommands->Release();
  766.  pExchExtCommands = NULL;
  767.  
  768.  
  769.  // enable or disable the command depending if the selected object is
  770.  // a folder or a "non-folder"  Enable if object is a folder.
  771.  if (ulType == MAPI_FOLDER)
  772.  {
  773.     EnableMenuItem(hMenu, cmdid, MF_BYCOMMAND | MF_ENABLED);
  774.  }
  775.  else
  776.  {
  777.     EnableMenuItem(hMenu, cmdid, MF_BYCOMMAND | MF_GRAYED);
  778.  }
  779.  
  780.  
  781. error_return:
  782.  
  783.  if (lpeid != NULL)
  784.     MAPIFreeBuffer(lpeid);
  785.  
  786. }
  787.  
  788.  
  789. ///////////////////////////////////////////////////////////////////////////////
  790. //    MyExchExtUserEvents::OnObjectChange()
  791. //
  792. //    Parameters
  793. //    pmecb  -- pointer to Exchange Callback Object
  794. //    
  795. //
  796. //    Purpose
  797. //    This function is called when the selection in the UI is to a different
  798. //    of object on the left pane.
  799. //
  800. //    Return Value - none
  801. //
  802. //    Comments
  803. //    OnObjectChange is called whenever the selection is changed between 
  804. //    objects in the left pane only.  Change in selection between folders, 
  805. //    subfolders or container object in the left pane will be reflected with a
  806. //    call to OnObjectChange.  Change in selection between objects (messages, 
  807. //    subfolders) in the right pane will not call OnObjectChange, only 
  808. //    OnSelectionChange.
  809. //
  810.  
  811. STDMETHODIMP_(VOID) MyExchExtUserEvents::OnObjectChange(LPEXCHEXTCALLBACK pmecb)
  812. {
  813.  // no need to handle this one
  814. }
  815.  
  816.  
  817.  
  818. ///////////////////////////////////////////////////////////////////////////////
  819. //  Helper functions to accomplish this extension's task
  820. //
  821.  
  822.  
  823. ///////////////////////////////////////////////////////////////////////////////
  824. //    GetFolderStats()
  825. //
  826. //    Parameters
  827. //    lpFolder      -- pointer to Folder of which to get statistics
  828. //    pulFolder     -- pointer to buffer to be filled with number of subfolders
  829. //    pulReadMsgs   -- pointer to buffer to be filled with number of read 
  830. //                     messages
  831. //    pulUnReadMsgs -- pointer to buffer to be filled with number of unread
  832. //                     messages
  833. //
  834. //    Purpose
  835. //    This function gathers information information about the given message
  836. //    folder.  It calculates the number of subfolders, read messages, and
  837. //    unread messages.
  838. //
  839. //    Return Value - none
  840. //
  841. //    Comments
  842. //    The Find window results folder only contains messages and not subfolders
  843. //    so it sets the number of subfolders to zero
  844. //
  845.  
  846. void GetFolderStats(LPMAPIFOLDER lpFolder, ULONG FAR * pulSubFolders, 
  847.                     ULONG FAR * pulReadMsgs, ULONG FAR * pulUnReadMsgs)
  848. {
  849.  HRESULT hr = 0;
  850.  LPMAPITABLE lpTable = NULL;
  851.  ULONG ulRows = 0;
  852.  UINT u;
  853.  LPSRowSet lpRows = NULL;
  854.  
  855.  enum { MESSAGE_FLAGS, STATTAGS };
  856.  
  857.  SizedSPropTagArray(STATTAGS,  MsgTags) =
  858.       { STATTAGS, 
  859.         { 
  860.          PR_MESSAGE_FLAGS        // contains read/unread flags
  861.         } 
  862.       };
  863.  
  864.  
  865.  hr = lpFolder->GetHierarchyTable(0, &lpTable);   // get table of subfolders
  866.  if (MAPI_E_NO_SUPPORT == hr)                     // Find window folder doesn't 
  867.     *pulSubFolders = 0;                           // support GetHierarchyTable
  868.     
  869.  else if (S_OK != hr)              // some other error
  870.     goto error_return;
  871.  
  872.  if (lpTable != NULL)
  873.  {
  874.     lpTable->GetRowCount(0, pulSubFolders);
  875.     lpTable->Release();
  876.     lpTable =NULL;
  877.  }
  878.  
  879.  hr = lpFolder->GetContentsTable(0, &lpTable);    // get table of messages
  880.  if (S_OK != hr)
  881.     goto error_return;
  882.  
  883.  hr = lpTable->SetColumns((LPSPropTagArray)&MsgTags, 0);
  884.  if (S_OK != hr)
  885.     goto error_return;
  886.  
  887.  lpTable->GetRowCount(0, &ulRows);
  888.  hr = lpTable->QueryRows(ulRows, 0, &lpRows);
  889.  if (S_OK != hr)
  890.     goto error_return;
  891.  
  892.  *pulReadMsgs = 0;
  893.  *pulUnReadMsgs = 0;
  894.  
  895.  for (u=0; u<ulRows; u++)
  896.  {
  897.  
  898.   if (MSGFLAG_READ & lpRows->aRow[u].lpProps[MESSAGE_FLAGS].Value.ul)
  899.     (*pulReadMsgs)++;
  900.   else
  901.     (*pulUnReadMsgs)++;
  902.  
  903.  }
  904.  
  905.  
  906. error_return:
  907.  
  908.  if (NULL != lpTable)
  909.     lpTable->Release();
  910.  
  911.  if (NULL != lpRows)
  912.     FreeProws(lpRows);
  913.  
  914. }
  915.