MSPNTFY.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:38k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*
  2.  *  M S P N T F Y . C
  3.  *
  4.  *  Code for doing internal cross-process notifications within the 
  5.  *  Sample Message Store provider.
  6.  *
  7.  *  Copyright 1992-1995 Microsoft Corporation.  All Rights Reserved.
  8.  */
  9. #include "msp.h"
  10. #include <stdarg.h>
  11. /* INTERNAL Function prototypes. */
  12. long STDAPICALLTYPE LSMSOQNotifCallback(LPVOID lpvContext, ULONG cNotif,
  13.     LPNOTIFICATION lpNotifs);
  14. static void EmptyTable(LPTABLEDATA lptbl, PLMR plmr);
  15. static HRESULT HrApplyOQNotifChanges(LPTABLEDATA lptbl, PONB ponbIn);
  16. static HRESULT HrGetOutgoingNotificationKey(PIMS pims, LPNOTIFKEY * lppKey);
  17. static HRESULT HrNotifyOnOutgoingQueue(PIMS pims, PEID peid, LPSRow prw,
  18.     ULONG ulTableEvent, FILETIME *pftBeforeUpdate);
  19. long STDAPICALLTYPE LSMSTblNotifCallback(LPVOID lpvContext, ULONG cNotif,
  20.     LPNOTIFICATION lpNotifs);
  21. HRESULT HrApplyTblNotifChanges(PIMS pims, PTNB ptnbIn);
  22. static HRESULT HrGetTableNotificationKey(PIMS pims, LPNOTIFKEY * lppKey);
  23. /*
  24.  *  EXTERNAL FUNCTIONS (called from outside this file).
  25.  */
  26. /* 
  27.  * HrCreateOGQueueMutex
  28.  *
  29.  *  Purpose
  30.  *      Create the outgoing queue mutex, and return it to the caller.
  31.  *
  32.  *  Arguments
  33.  *      phQMutex: Pointer to the location to return the new mutex.
  34.  *
  35.  *  Returns:
  36.  *      HRESULT: Will return an error only if the CreateMutex call fails.
  37.  */
  38. HRESULT HrCreateOGQueueMutex(HANDLE *phQMutex)
  39. {
  40.     HRESULT hr = hrSuccess;
  41.     HANDLE hMutex;
  42.     LPTSTR szMutexName = "SMS_OGQUEUEFILE_MUTEX";
  43.     hMutex = CreateMutex(NULL, FALSE, szMutexName);
  44.     if (hMutex)
  45.         *phQMutex = hMutex;
  46.     #ifndef WIN16
  47.     else
  48.     {
  49.         TraceSz1("SampleMS: HrCreateOGQueueMutex: call to"
  50.             " CreateMutex failed (error %08lX)", GetLastError());
  51.         
  52.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  53.     }
  54.     #endif
  55.     DebugTraceResult(HrCreateOGQueueMutex, hr);
  56.     return hr;
  57. }
  58. /*
  59.  * HrSetupPrivateNotifications
  60.  *
  61.  *  Purpose
  62.  *      Setup two private channels via the MAPI notification engine to
  63.  *      tell other processes running against this store when 1) the 
  64.  *      outgoing queue changes and 2) when contents and hierarchy tables
  65.  *      change. We communicate between the multiple client processes and
  66.  *      the one spooler process. For the outgoing queue, we use a key that
  67.  *      is the full pathname to the outgoing queue file. For the other
  68.  *      tables, we use a unique 16-byte ID. Remember the connections so
  69.  *      that we can Unsubscribe when we shutdown the store.
  70.  *
  71.  *  Arguments
  72.  *      pims: a pointer to the message store object.
  73.  *
  74.  *  Side Effects
  75.  *      Fills in the ulOQConn and ulTblConn members of pims.
  76.  *
  77.  *  Returns
  78.  *      HRESULT
  79.  */
  80. HRESULT HrSetupPrivateNotifications(PIMS pims)
  81. {
  82.     HRESULT hr;
  83.     LPMAPIADVISESINK lpAdvise;
  84.     ULONG ulOQConn = 0;
  85.     ULONG ulTblConn = 0;
  86.     LPNOTIFKEY lpKey = NULL;
  87.     /* Use the MAPI notification engine to tell myself */
  88.     /* (across processes) when to update outgoing queue. */
  89.     /* The key is the path to the disk cache of the outbound queue */
  90.     hr = HrGetOutgoingNotificationKey(pims, &lpKey);
  91.     if (hr != hrSuccess)
  92.         goto exit;
  93.     hr = HrAllocAdviseSink(&LSMSOQNotifCallback, (LPVOID) pims, &lpAdvise);
  94.     if (hr != hrSuccess)
  95.         goto exit;
  96.     hr = pims->psup->lpVtbl->Subscribe(pims->psup, lpKey,
  97.         fnevExtended, 0L, lpAdvise, &ulOQConn);
  98.     /* Always release; mapi will have addref'ed it during Subscribe */
  99.     UlRelease(lpAdvise);
  100.     if (hr != hrSuccess)
  101.         goto exit;
  102.     FreeNull(lpKey);
  103.     lpKey = NULL;
  104.     /* Now, setup notifications for the other tables. */
  105.     hr = HrGetTableNotificationKey(pims, &lpKey);
  106.     if (hr != hrSuccess)
  107.         goto exit;
  108.     hr = HrAllocAdviseSink(&LSMSTblNotifCallback, (LPVOID) pims, &lpAdvise);
  109.     if (hr != hrSuccess)
  110.         goto exit;
  111.     hr = pims->psup->lpVtbl->Subscribe(pims->psup, lpKey,
  112.         fnevExtended, 0L, lpAdvise, &ulTblConn);
  113.     /* Always release; mapi will have addref'ed it during Subscribe */
  114.     UlRelease(lpAdvise);
  115.     if (hr != hrSuccess)
  116.         goto exit;
  117. exit:
  118.     FreeNull(lpKey);
  119.     if (hr == hrSuccess)
  120.     {
  121.         /* Remember our use of the notification engine. */
  122.         pims->ulOQConn = ulOQConn;
  123.         pims->ulTblConn = ulTblConn;
  124.     }
  125.     else if (ulOQConn != 0)
  126.         (void) pims->psup->lpVtbl->Unsubscribe(pims->psup, ulOQConn);
  127.     DebugTraceResult(HrSetupPrivateNotifications, hr);
  128.     return hr;
  129. }
  130. /*
  131.  * HrUpdateOutgoingQueue
  132.  *
  133.  *  Purpose
  134.  *      Updates the outgoing queue based on the information given. If the
  135.  *      outgoing queue table is not open, or is out-of-date with respect to
  136.  *      the outgoing queue file on disk, initializes the table. The function
  137.  *      then applies the change requested to the table, writes the table on
  138.  *      disk, and notifies other processes of the change.
  139.  *
  140.  *  Arguments
  141.  *      pims: A pointer to the message store object.
  142.  *      pimsg: For a TABLE_ROW_ADDED event, a pointer to the message being
  143.  *          added to the queue; otherwise, this parameter should be NULL.
  144.  *      peid: For a TABLE_ROW_DELETED event, a pointer to the entryid of the
  145.  *          message being deleted from the queue; otherwise, this parameter
  146.  *          should be NULL.
  147.  *      ulTableEvent: The type of update event: Either TABLE_ROW_ADDED or
  148.  *          TABLE_ROW_DELETED.
  149.  *
  150.  *  Returns
  151.  *      HRESULT
  152.  */
  153. HRESULT HrUpdateOutgoingQueue(PIMS pims, PIMSG pimsg, PEID peid,
  154.     ULONG ulTableEvent)
  155. {
  156.     HRESULT hr = hrSuccess;
  157.     LPTABLEDATA lptbl;
  158.     SRow srNewRow = {0, 0, NULL};
  159.     LPSRow prw;
  160.     BOOL fInMutex = FALSE;
  161.     FILETIME ftBeforeUpdate;
  162.     /* If the file mutex doesn't yet exist on this process, create it. */
  163.     if (pims->hOGQueueMutex == NULL)
  164.     {
  165.         hr = HrCreateOGQueueMutex(&pims->hOGQueueMutex);
  166.         if (hr != hrSuccess)
  167.             goto exit;
  168.     }
  169.     /* Get the file mutex so that we can use the file (and change it) */
  170.     /* without crossing paths with another process. */
  171.     WaitForSingleObject(pims->hOGQueueMutex, INFINITE);
  172.     fInMutex = TRUE;
  173.     /* This routine will open the outgoing queue table if it's not already */
  174.     /* open in this process, and will leave the opened copy around in pims. */
  175.     if (pims->lptblOutgoing == NULL)
  176.     {
  177.         hr = HrNewOutgoingTableData(pims);
  178.         if (hr != hrSuccess)
  179.             goto exit;
  180.     }
  181.     lptbl = pims->lptblOutgoing;
  182.     ftBeforeUpdate = pims->ftOGQueue;
  183.     if (ulTableEvent == TABLE_ROW_ADDED)
  184.     {
  185.         ULONG cValues;
  186.         AssertSz(pimsg, "A msg should be provided on an add");
  187.         AssertSz(peid == NULL, "No entryid should be provided on an add");
  188.         hr = pimsg->lpVtbl->GetProps(pimsg, (LPSPropTagArray) &sptaOutgoing,
  189.             0, /* ansi */
  190.             &cValues, &srNewRow.lpProps);
  191.     
  192.         if (HR_FAILED(hr))          /* Ignore warnings from GetProps. */
  193.             goto exit;
  194.     
  195.         srNewRow.cValues = cValues;
  196.     
  197.         hr = lptbl->lpVtbl->HrModifyRow(lptbl, &srNewRow);
  198.         if (hr != hrSuccess)
  199.             goto exit;
  200.         prw = &srNewRow;
  201.     }
  202.     else
  203.     {
  204.         AssertSz(ulTableEvent == TABLE_ROW_DELETED,
  205.             "Bad event type received");
  206.         AssertSz(pimsg == NULL, "No msg should be provided on a delete");
  207.         AssertSz(peid, "An entryid should be provided on a delete");
  208.         /* remove it from the outgoing queue */
  209.         hr = HrRemoveRow(lptbl, peid);
  210.         if (hr != hrSuccess)
  211.             goto exit;
  212.         prw = NULL;
  213.     }
  214.     hr = HrWriteTableOnDisk(lptbl, (POBJ) pims, NULL, szOutgoingFileName);
  215.     if (hr != hrSuccess)
  216.         goto exit;
  217.     /* Update the last mod time of the table inside this process's */
  218.     /* message store object. */
  219.     hr = HrGetFileModTime(pims->szStorePath, szOutgoingFileName,
  220.         &pims->ftOGQueue);
  221.     if (hr != hrSuccess)
  222.         goto exit;
  223.     hr = HrNotifyOnOutgoingQueue(pims, peid, prw, ulTableEvent,
  224.         &ftBeforeUpdate);
  225. exit:
  226.     if (fInMutex)
  227.         ReleaseMutex(pims->hOGQueueMutex);
  228.     LMFree(&pims->lmr, srNewRow.lpProps);
  229.     DebugTraceResult(HrUpdateOutgoingQueue, hr);
  230.     return hr;
  231. }
  232. /*
  233.  * HrNewOutgoingTableData
  234.  *
  235.  * Purpose  Checks the outgoing table data object in the message store.
  236.  *          If there isn't one, creates it and initializes it from disk.
  237.  *          If there is one, empties it, and re-initializes it from disk.
  238.  *          Should be inside the outgoing queue mutex during this function.
  239.  *
  240.  * Parameters
  241.  *  pims    A pointer to the message store object.
  242.  *
  243.  * Side Effects
  244.  *          Fills in the lptblOutgoing member of the pims.
  245.  *          Updates pims->ftOGQueue with the last mod time of the 
  246.  *          outgoing queue file.
  247.  */
  248. HRESULT HrNewOutgoingTableData(PIMS pims)
  249. {
  250.     HRESULT hr = hrSuccess;
  251.     LPTABLEDATA lptbl = pims->lptblOutgoing;
  252.     BOOL fTableCreated = FALSE;
  253.     if (!lptbl)
  254.     {
  255.         PINST pinst;
  256.         SCODE sc = S_OK;
  257.     
  258.         /* The table doesn't exist. Create it. */
  259.         pinst = (PINST) PvGetInstanceGlobals();
  260.     
  261.         if (pinst == NULL)
  262.         {
  263.             hr = ResultFromScode(MAPI_E_CALL_FAILED);
  264.             goto exit;
  265.         }
  266.     
  267.         sc = CreateTable((LPIID) &IID_IMAPITableData, pims->lmr.lpAllocBuf,
  268.             pims->lmr.lpAllocMore, pims->lmr.lpFreeBuf, pinst->lpmalloc,
  269.             TBLTYPE_DYNAMIC, PR_INSTANCE_KEY, (LPSPropTagArray) &sptaOutgoing,
  270.             &lptbl);
  271.     
  272.         if (sc != S_OK)
  273.         {
  274.             hr = ResultFromScode(sc);
  275.             goto exit;
  276.         }
  277.         fTableCreated = TRUE;
  278.     }
  279.     else
  280.     {
  281.         /* The table exists already. Delete all rows from it. */
  282.         EmptyTable(lptbl, &pims->lmr);
  283.     }
  284.     /* read the table in from disk */
  285.     /* outgoing queue tables can't be regenerated, so any error reading */
  286.     /* the table is fatal. We lose all messages in the OG queue if this */
  287.     /* function has an error. */
  288.     hr = HrReadTableFromDisk(lptbl, (POBJ) pims, NULL, OUTGOING_COLUMNS,
  289.         szOutgoingFileName);
  290.     if (hr != hrSuccess)
  291.     {
  292.         TraceSz("SMS: Bad OG Queue data on disk.");
  293.         goto exit;
  294.     }
  295.     /* Verify that all messages in the table actually exist on disk. If */
  296.     /* not, remove the row(s) and re-write the table to disk. */
  297.     hr = HrSyncOutgoingTable(lptbl, pims);
  298.     if (hr != hrSuccess)
  299.         goto exit;
  300.     /* Save away the last mod time of the table inside this process's */
  301.     /* message store object. */
  302.     hr = HrGetFileModTime(pims->szStorePath, szOutgoingFileName,
  303.         &pims->ftOGQueue);
  304.     if (hr != hrSuccess)
  305.         goto exit;
  306.     pims->lptblOutgoing = lptbl;
  307. exit:
  308.     if (hr != hrSuccess && fTableCreated)
  309.         UlRelease(lptbl);
  310.     DebugTraceResult(HrNewOutgoingTableData, hr);
  311.     return hr;
  312. }
  313. /*
  314.  * INTERNAL Functions (called from within this file ONLY).
  315.  *
  316.  */
  317. /*----------------------------------------------------------------------+
  318. |                                                                       |
  319. |           CONTENTS AND HIERARCHY TABLE NOTIFICATION HANDLING          |
  320. |                                                                       |
  321. +----------------------------------------------------------------------*/
  322. /*
  323.  * LSMSTblNotifCallback
  324.  *
  325.  *  Purpose
  326.  *      Update the contents or hierarchy table associated with the folder eid
  327.  *      passed across. We should receive a notification when the tabledata
  328.  *      object needs to have a row added, deleted or modified. Calls
  329.  *      ChangeTable after decoding the notification.
  330.  *
  331.  *  Arguments
  332.  *      lpvContext: A pointer to the message store object to use. We need
  333.  *          to verify that the object is still valid before using it.
  334.  *      cNotif: The number of notifications to process.
  335.  *      lpNotif: A pointer to an array of NOTIFICATION structures.
  336.  *
  337.  *  Returns
  338.  *      LONG: Always returns 0.
  339.  */
  340. long STDAPICALLTYPE LSMSTblNotifCallback(LPVOID lpvContext, ULONG cNotif,
  341.     LPNOTIFICATION lpNotif)
  342. {
  343.     PIMS pims = (PIMS) lpvContext;
  344.     SCODE sc = S_OK;
  345.     HRESULT hr = hrSuccess;
  346.     PTNB ptnb;
  347.     /*
  348.      * Our code sends one extended notification at a time.
  349.      * The notification consists of the table event that occurred along with
  350.      * an object notification containing the entryids we need. We only use
  351.      * two of the entryids in the object notification. The ParentID fields
  352.      * refer to the parent folder of the table we need to update. The EntryID
  353.      * fields refer to the object that changed within the folder. The
  354.      * ulObjType field will be either MAPI_MESSAGE (for contents table
  355.      * changes) or MAPI_FOLDER (for hierarchy table changes).  All other
  356.      * fields in the structure are unused and should be set to 0.
  357.      */
  358.     if (IMS_IsInvalid(pims)
  359.         || cNotif != 1
  360.         || (IsBadReadPtr(lpNotif, ((UINT) cNotif) * sizeof(NOTIFICATION)))
  361.         || lpNotif->ulEventType != fnevExtended
  362.         || lpNotif->info.ext.ulEvent != 0)
  363.         return 0;
  364.     ptnb = (PTNB) lpNotif->info.ext.pbEventParameters;
  365.     if (IsBadReadPtr(ptnb, CbNewTNB(0))
  366.         || IsBadReadPtr(ptnb, CbTNB(ptnb)))
  367.         return 0;
  368.     IMS_EnterCriticalSection(pims);
  369.     hr = HrApplyTblNotifChanges(pims, ptnb);
  370.     IMS_LeaveCriticalSection(pims);
  371.     if (hr != hrSuccess)
  372.         sc = GetScode(hr);
  373.     return sc;
  374. }
  375. /* 
  376.  * HrApplyTblNotifChanges
  377.  *
  378.  * Purpose
  379.  *  This function relocates and validates the internal object notification
  380.  *  passed in, and then calls ChangeTable to actually update any open tables
  381.  *  within this process with the change given. The notification needs to be
  382.  *  relocated because the pointers from the other process may not be valid
  383.  *  on this process.
  384.  *
  385.  * Parameters
  386.  *  pims: A pointer to the message store object.
  387.  *  ptnbIn: A pointer to the table notification block (TNB) containing the
  388.  *      data we need in order to update any open tables om this process.
  389.  *
  390.  * Returns: validation errors or hrSuccess.
  391.  */
  392. HRESULT HrApplyTblNotifChanges(PIMS pims, PTNB ptnbIn)
  393. {
  394.     HRESULT hr = hrSuccess;
  395.     SCODE sc = S_OK;
  396.     LPNOTIFICATION lpntf;
  397.     PTNB ptnb = NULL;
  398.     OBJECT_NOTIFICATION *pon;
  399.     ULONG cb;
  400.     /* Allocate a new notification block, and copy the data over before */
  401.     /* relocation. */
  402.     hr = HrAlloc(CbNewTNB(ptnbIn->cbNtf), &ptnb);
  403.     if (hr != hrSuccess)
  404.         goto ret;
  405.     memcpy(ptnb, ptnbIn, (UINT) CbNewTNB(ptnbIn->cbNtf));
  406.     lpntf = (LPNOTIFICATION) ptnb->abNtf;
  407.     if (lpntf->ulEventType != fnevObjectModified)
  408.     {
  409.         TraceSz1("SMS: HrApplyTblNotifChanges: Bad ulEventType %08lX "
  410.             "received", lpntf->ulEventType);
  411.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  412.         goto ret;
  413.     }
  414.     /* Relocate the notification into the address space of this process. */
  415.     /* We passed along the memory offset of the originating process to allow */
  416.     /* this code to work. Note that ScRelocNotifications currently only */
  417.     /* works from "bad" addresses to "good" addresses. The code assumes that */
  418.     /* pointers are "bad" inside the notification, and that after conversion, */
  419.     /* they are valid. */
  420.     sc = ScRelocNotifications(1, lpntf, ptnb->pvRef, (LPVOID) lpntf, &cb);
  421.     if (sc != S_OK)
  422.     {
  423.         hr = ResultFromScode(sc);
  424.         goto ret;
  425.     }
  426.     if (ptnb->cbNtf != cb)
  427.     {
  428.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  429.         goto ret;
  430.     }
  431.     pon = (OBJECT_NOTIFICATION *) &(lpntf->info.obj);
  432.     if (pon->ulObjType != MAPI_MESSAGE && pon->ulObjType != MAPI_FOLDER)
  433.     {
  434.         TraceSz1("SMS: HrApplyTblNotifChanges: unexpected Object Type %08lX",
  435.             pon->ulObjType);
  436.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  437.         goto ret;
  438.     }
  439.     if (    FIsInvalidEID(pon->cbParentID, (PEID) pon->lpParentID, pims)
  440.         ||  !FIsFolder((PEID) pon->lpParentID))
  441.     {
  442.         TraceSz("SMS: HrApplyTblNotifChanges: invalid parent entryid");
  443.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  444.         goto ret;
  445.     }
  446.     /* TABLE_CHANGED events don't require a lpEntryID, because multiple
  447.      * objects have changed. ChangeTable() simply validates all rows in
  448.      * the table against the files on disk.
  449.      */
  450.     if (    ptnb->ulTableEvent != TABLE_CHANGED
  451.         &&  FIsInvalidEID(pon->cbEntryID, (PEID) pon->lpEntryID, pims))
  452.     {
  453.         TraceSz("SMS: HrApplyTblNotifChanges: invalid entryid");
  454.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  455.         goto ret;
  456.     }
  457.     ChangeTable(pims, (PEID) pon->lpParentID, (PEID) pon->lpEntryID,
  458.         pon->ulObjType, ptnb->ulTableEvent, FALSE);
  459. ret:
  460.     FreeNull(ptnb);
  461.     DebugTraceResult(HrApplyTblNotifChanges, hr);
  462.     return hr;
  463. }
  464. /* 
  465.  * HrSendNotif
  466.  *
  467.  * Purpose
  468.  *  This function constructs and sends a notification to other active Sample
  469.  *  Message Store processes open on this message store file. The notification
  470.  *  describes a change (add, delete, or modify) to either a message (contents
  471.  *  table) or folder (hierarchy table). The receiver will need to get two
  472.  *  entryids, one for the parent folder of the changed object, and one for
  473.  *  the changed object itself. The receiver also needs to know whether the
  474.  *  changed object is a message or a folder, and what type of change occurred:
  475.  *  add, delete, modify, or change (contents tables only).
  476.  *
  477.  * Parameters
  478.  *  pims: Pointer to the message store object.
  479.  *  peidParent: The entryid of the folder containing the table to update and
  480.  *              the object that changed.
  481.  *  peidObject: The entryid of the object that changed. May be NULL when
  482.  *              sending a TABLE_CHANGED notification (contents tables only).
  483.  *  ulTableEvent: TABLE_ROW_ADDED, TABLE_ROW_DELETED, TABLE_ROW_MODIFIED, or
  484.  *              TABLE_CHANGED (TABLE_CHANGED only works on contents tables).
  485.  *  ulObjType: The type of object that changed. This implies the type of table
  486.  *              to update. May be MAPI_MESSAGE (contents table) or MAPI_FOLDER
  487.  *              (hierarchy table).
  488.  *
  489.  * Returns: Memory and disk errors or success.
  490.  */
  491. HRESULT HrSendNotif(PIMS pims, PEID peidParent, PEID peidObject,
  492.     ULONG ulTableEvent, ULONG ulObjType)
  493. {
  494.     HRESULT hr = hrSuccess;
  495.     SCODE sc;
  496.     LPNOTIFKEY lpKey = NULL;
  497.     NOTIFICATION ntfTemp;
  498.     NOTIFICATION ntf;
  499.     PTNB ptnb = NULL;
  500.     ULONG cbNtf;
  501.     ULONG cbOut;
  502.     ULONG ulFlags = 0;
  503.     /* Get the key */
  504.     hr = HrGetTableNotificationKey(pims, &lpKey);
  505.     if (hr != hrSuccess)
  506.         goto ret;
  507.     /*
  508.      * Our code sends one extended notification at a time. The notification
  509.      * consists of the table event that occurred along with an object
  510.      * notification containing the entryids we need. We send the type of
  511.      * table event as part of our extended notification structure, and
  512.      * package the object notification always as "fnevObjectModified". We
  513.      * only use two of the entryids in the object notification. The ParentID
  514.      * fields refer to the parent folder of the table we need to update. The
  515.      * EntryID fields refer to the object that changed within the folder. The
  516.      * ulObjType field will be either MAPI_MESSAGE (for contents table
  517.      * changes) or MAPI_FOLDER (for hierarchy table changes).  All other
  518.      * fields in the structure are unused and should be set to 0.
  519.      */
  520.     memset(&ntfTemp, 0, sizeof(NOTIFICATION));
  521.     /* We always send the same type of event here. This is enough to get the
  522.      * notification code to count and relocate what we send. We send the real
  523.      * table event in ptnb->ulTableEvent (see below).
  524.      */
  525.     ntfTemp.ulEventType = fnevObjectModified;
  526.     ntfTemp.info.obj.ulObjType  = ulObjType;
  527.     ntfTemp.info.obj.lpParentID = (LPENTRYID) peidParent;
  528.     ntfTemp.info.obj.cbParentID = CbEID(peidParent);
  529.     if (ulTableEvent != TABLE_CHANGED)
  530.     {
  531.         ntfTemp.info.obj.lpEntryID  = (LPENTRYID) peidObject;
  532.         ntfTemp.info.obj.cbEntryID  = CbEID(peidObject);
  533.     }
  534.     sc = ScCountNotifications(1, &ntfTemp, &cbNtf);
  535.     if (sc != S_OK)
  536.     {                   
  537.         hr = ResultFromScode(sc);
  538.         goto ret;
  539.     }
  540.     hr = HrAlloc(CbNewTNB(cbNtf), &ptnb);
  541.     if (hr != hrSuccess)
  542.         goto ret;
  543.     /* Here's where we send the table event. It's either TABLE_ROW_ADDED,
  544.      * TABLE_ROW_DELETED, TABLE_ROW_MODIFIED, or TABLE_CHANGED.
  545.      */
  546.     ptnb->ulTableEvent = ulTableEvent;
  547.     ptnb->cbNtf = cbNtf;
  548.     sc = ScCopyNotifications(1, &ntfTemp, (LPVOID) ptnb->abNtf, &cbOut);
  549.     if (sc != S_OK)
  550.     {
  551.         hr = ResultFromScode(sc);
  552.         goto ret;
  553.     }
  554.     AssertSz(cbOut == cbNtf, "ScCopyNotifications used a different # of bytes "
  555.         "than ScCountNotifications returned.");
  556.     /* Pass across the notification's memory offset so that the receiving */
  557.     /* process can relocate the notification to its address space. */
  558.     ptnb->pvRef = (LPVOID) ptnb->abNtf;
  559.     
  560.     ntf.ulEventType = fnevExtended;
  561.     ntf.info.ext.ulEvent = 0;
  562.     ntf.info.ext.cb = CbTNB(ptnb);
  563.     ntf.info.ext.pbEventParameters = (LPBYTE) ptnb;
  564.     hr = pims->psup->lpVtbl->Notify(pims->psup, lpKey, 1, &ntf, &ulFlags);
  565. ret:
  566.     FreeNull(lpKey);
  567.     FreeNull(ptnb);
  568.     DebugTraceResult(HrSendNotif, hr);
  569.     return hr;
  570. }
  571. /*
  572.  * HrGetTableNotificationKey
  573.  *
  574.  * Purpose
  575.  *  Generate and return the notification key that will allow cross-process
  576.  *  notifications for changes to any contents or hierarchy tables within a
  577.  *  store. The memory returned should be freed with FreeNull. This
  578.  *  notification key needs to work for processes attached to this particular
  579.  *  message store, and should only receive notifications having to do with
  580.  *  changes to tables. The key contains the store guid (unique for this store)
  581.  *  preceeded by a ULONG with 0x0000ABCD in it. Note that the choice of
  582.  *  0x0000ABCD is arbitrary. As long as the sender sends to the same key as
  583.  *  the receiver listens to, and no unexpected sender sends to that key,
  584.  *  we're fine.
  585.  *
  586.  * Parameters
  587.  *  pims        pointer to the message store object.
  588.  *  lppKey      pointer to the location to return the new key.
  589.  */
  590. static HRESULT HrGetTableNotificationKey(PIMS pims, LPNOTIFKEY * lppKey)
  591. {
  592.     HRESULT hr = hrSuccess;
  593.     LPNOTIFKEY lpKey = NULL;
  594.     ULONG cb;                   /* number of bytes in the key */
  595.     /* allocate space for the key */
  596.     cb = sizeof(ULONG) + sizeof(MAPIUID);
  597.     hr = HrAlloc(CbNewNOTIFKEY(cb), (PPV) &lpKey);
  598.     if (hr != hrSuccess)
  599.         goto exit;
  600.     *((ULONG *) &(lpKey->ab[0])) = 0x0000ABCD;
  601.     GetResourceUID(pims, (MAPIUID *) &(lpKey->ab[sizeof(ULONG)]));
  602.     lpKey->cb = cb;
  603. exit:
  604.     if (HR_FAILED(hr))
  605.     {
  606.         FreeNull(lpKey);
  607.         lpKey = NULL;
  608.     }
  609.     *lppKey = lpKey;
  610.     DebugTraceResult(HrGetTableNotificationKey, hr);
  611.     return hr;
  612. }
  613. /*----------------------------------------------------------------------+
  614. |                                                                       |
  615. |               OUTGOING QUEUE NOTIFICATION HANDLING                    |
  616. |                                                                       |
  617. +----------------------------------------------------------------------*/
  618. /*
  619.  * LSMSOQNotifCallback
  620.  *
  621.  *  Purpose
  622.  *      Update the outgoing queue table associated with the process that
  623.  *      the spooler is using. We should receive a notification when the
  624.  *      tabledata object needs to have a row added or deleted. If the
  625.  *      tabledata object exists on the message store object, then call
  626.  *      HrModifyRow (when a row is added), or HrRemoveRow (when a row is
  627.  *      deleted) to update the table appropriately.
  628.  *
  629.  *  Arguments
  630.  *      lpvContext: A pointer to the message store object to use. We need
  631.  *          to verify that the object is still valid before using it.
  632.  *      cNotif: The number of notifications to process.
  633.  *      lpNotif: A pointer to an array of NOTIFICATION structures.
  634.  *
  635.  *  Returns
  636.  *      LONG: Always returns 0.
  637.  */
  638. long STDAPICALLTYPE LSMSOQNotifCallback(LPVOID lpvContext, ULONG cNotif,
  639.     LPNOTIFICATION lpNotif)
  640. {
  641.     PIMS pims = (PIMS) lpvContext;
  642.     SCODE sc = S_OK;
  643.     HRESULT hr = hrSuccess;
  644.     FILETIME ftCurrent;
  645.     BOOL fInMutex = FALSE;
  646.     PONB ponb;
  647.     /* Our code sends one extended notification at a time. This */
  648.     /* extended notification contains two filetimes (the time */
  649.     /* before the outgoing queue file was modified, and the time after */
  650.     /* the change was made), and a standard notification with the change */
  651.     /* to apply to the table. It should be an fnevTableModified, and */
  652.     /* should be either TABLE_ROW_ADDED or TABLE_ROW_DELETED. If we ever */
  653.     /* receive anything other than this, the code must change. */
  654.     if (IMS_IsInvalid(pims)
  655.         || cNotif != 1
  656.         || (IsBadReadPtr(lpNotif, ((UINT) cNotif) * sizeof(NOTIFICATION)))
  657.         || lpNotif->ulEventType != fnevExtended
  658.         || lpNotif->info.ext.ulEvent != 0)
  659.         return 0;
  660.     ponb = (PONB) lpNotif->info.ext.pbEventParameters;
  661.     if (IsBadReadPtr(ponb, CbNewONB(0))
  662.         || IsBadReadPtr(ponb, CbONB(ponb)))
  663.         return 0;
  664.     IMS_EnterCriticalSection(pims);
  665.     /* Check to see if the outgoing queue table data is open. If it isn't, */
  666.     /* there is nothing to do, because the current table will be read and */
  667.     /* initialized from disk when the spooler opens it. We're done. */
  668.     if (!pims->lptblOutgoing)
  669.         goto exit;
  670.     /* If the file mutex doesn't yet exist on this process, create it. */
  671.     if (pims->hOGQueueMutex == NULL)
  672.     {
  673.         hr = HrCreateOGQueueMutex(&pims->hOGQueueMutex);
  674.         if (hr != hrSuccess)
  675.             goto exit;
  676.     }
  677.     /* Get the file mutex so that we can use the file (and change it) */
  678.     /* without crossing paths with another process. */
  679.     WaitForSingleObject(pims->hOGQueueMutex, INFINITE);
  680.     fInMutex = TRUE;
  681.     /* Get time that the file was last modified */
  682.     hr = HrGetFileModTime(pims->szStorePath, szOutgoingFileName, &ftCurrent);
  683.     if (hr != hrSuccess)
  684.         goto exit;
  685.     /* If the time that this process last read the file is the same as */
  686.     /* the time that the file was last modified, then don't do anything */
  687.     /* because we already have all changes, including the one sent to us */
  688.     /* in this notification. This can happen because we picked up two */
  689.     /* changes at once, or because this process is actually the same */
  690.     /* process that sent the notification. */
  691.     if (CompareFileTime(&ftCurrent, &pims->ftOGQueue) == 0)
  692.         goto exit;
  693.     /* If the time that this process last read the file is the same as */
  694.     /* the time that the other process read the file, then we can simply */
  695.     /* apply the changes sent in the notification itself, and update our */
  696.     /* time to the time after update sent in the notification. */
  697.     /* If the times are different, or there is a problem applying the */
  698.     /* changes, then reconstruct the table from the on-disk copy. */
  699.     if ((CompareFileTime(&ponb->ftBeforeUpdate, &pims->ftOGQueue) == 0)
  700.         && (HrApplyOQNotifChanges(pims->lptblOutgoing, ponb) == hrSuccess))
  701.     {
  702.         pims->ftOGQueue = ponb->ftAfterUpdate;
  703.     }
  704.     else
  705.     {
  706.         hr = HrNewOutgoingTableData(pims);
  707.         if (hr != hrSuccess)
  708.             goto exit;
  709.     }
  710. exit:
  711.     if (fInMutex)
  712.         ReleaseMutex(pims->hOGQueueMutex);
  713.     IMS_LeaveCriticalSection(pims);
  714.     if (hr != hrSuccess)
  715.         sc = GetScode(hr);
  716.     return sc;
  717. }
  718. /*
  719.  * EmptyTable
  720.  *
  721.  *  Purpose
  722.  *      Deletes all rows from the table data object given.
  723.  *      Helper function for the outgoing queue table notification callback
  724.  *      routine.
  725.  *
  726.  *  Arguments
  727.  *      lptbl: A pointer to the tabledata object to empty.
  728.  *      plmr: A pointer to the linked memory routines.
  729.  *
  730.  *  Returns
  731.  *      void.
  732.  */
  733. static void EmptyTable(LPTABLEDATA lptbl, PLMR plmr)
  734. {
  735.     HRESULT hr;
  736.     LPSRow lpsRow;
  737.     LPSPropValue pval;
  738.     LPSPropValue pvalMac;
  739.     while (TRUE)
  740.     {
  741.         /* Get the first row. Note that as we delete rows, this will */
  742.         /* keep giving us a new row. */
  743.         hr = lptbl->lpVtbl->HrEnumRow(lptbl, 0, &lpsRow);
  744.         if (hr != hrSuccess)
  745.         {
  746.             TraceSz1("Sample MS: EmptyTable: HrEnumRow failed with"
  747.                 " sc == %s", SzDecodeScode(GetScode(hr)));
  748.             break;
  749.         }
  750.         /* The table is empty when no row is returned */
  751.         if (!lpsRow)
  752.             break;
  753.         /* find the entryid in the property value array */
  754.         pval = lpsRow->lpProps;
  755.         pvalMac = pval + lpsRow->cValues;
  756.         for (; pval < pvalMac; ++pval)
  757.             if (pval->ulPropTag == PR_INSTANCE_KEY)
  758.                 break;
  759.         /* Every row should contain an inst key. It is the index property. */
  760.         if (pval == pvalMac)
  761.         {
  762.             TrapSz("No PR_INSTANCE_KEY found in the table row");
  763.             break;
  764.         }
  765.         /* delete this row from the table */
  766.         hr = lptbl->lpVtbl->HrDeleteRow(lptbl, pval);
  767.         LMFree(plmr, lpsRow);
  768.         lpsRow = NULL;
  769.         if (hr != hrSuccess)
  770.         {
  771.             TraceSz1("Sample MS: EmptyTable: HrDeleteRow failed with"
  772.                 " error %s", SzDecodeScode(GetScode(hr)));
  773.             break;
  774.         }
  775.     }
  776.     return;
  777. }
  778. /*
  779.  * HrApplyOQNotifChanges
  780.  *
  781.  *  Purpose
  782.  *      Helper function of the notification callback routine. If the 
  783.  *      table on disk hasn't changed except for the notification given,
  784.  *      then we can update the outgoing queue table data directly instead
  785.  *      of re-reading the table from disk. This function modifies the
  786.  *      table data directly by calling HrModifyRow (when a row is added),
  787.  *      or HrRemoveRow (when a row is deleted).
  788.  *      Note that we have to convert the notification inside the ONB so
  789.  *      that the pointers are valid. Also note that we may NOT modify the
  790.  *      notification data at all; therefore, we must copy the data before
  791.  *      changing it.
  792.  *
  793.  *  Arguments
  794.  *      lptbl: a pointer to the table data object to update.
  795.  *      ponbIn: a pointer to the ONB received in the callback.
  796.  *
  797.  *  Returns
  798.  *      HRESULT
  799.  */
  800. static HRESULT HrApplyOQNotifChanges(LPTABLEDATA lptbl, PONB ponbIn)
  801. {
  802.     HRESULT hr = hrSuccess;
  803.     SCODE sc;
  804.     ULONG cb;
  805.     LPNOTIFICATION lpntf;
  806.     PONB ponb = NULL;
  807.     /* Allocate a new notification block, and copy the data over before */
  808.     /* relocation. */
  809.     hr = HrAlloc(CbNewONB(ponbIn->cbNtf), &ponb);
  810.     if (hr != hrSuccess)
  811.         goto exit;
  812.     memcpy(ponb, ponbIn, (UINT) CbNewONB(ponbIn->cbNtf));
  813.     lpntf = (LPNOTIFICATION) ponb->abNtf;
  814.     /* Relocate the notification into the address space of this process. */
  815.     /* We passed along the memory offset of the originating process to allow */
  816.     /* this code to work. Note that ScRelocNotifications currently only */
  817.     /* works from "bad" addresses to "good" addresses. The code assumes that */
  818.     /* pointers are "bad" inside the notification, and that after conversion, */
  819.     /* they are valid. */
  820.     sc = ScRelocNotifications(1, lpntf, ponb->pvRef, (LPVOID) lpntf, &cb);
  821.     if (sc != S_OK)
  822.     {
  823.         hr = ResultFromScode(sc);
  824.         goto exit;
  825.     }
  826.     if (ponb->cbNtf != cb)
  827.     {
  828.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  829.         goto exit;
  830.     }
  831.     /* We don't expect any events other than those for a table. */
  832.     if (lpntf->ulEventType != fnevTableModified)
  833.     {
  834.         TraceSz1("SMS: HrApplyOQNotifChanges: unexpected ulEventType %08lX",
  835.             lpntf->ulEventType);
  836.         hr = ResultFromScode(MAPI_E_CALL_FAILED);
  837.         goto exit;
  838.     }
  839.     switch (lpntf->info.tab.ulTableEvent)
  840.     {
  841.     case TABLE_ROW_DELETED:
  842.         /* delete the row from the table according to the index */
  843.         /* property value in the notification structure */
  844.         hr = lptbl->lpVtbl->HrDeleteRow(lptbl, &lpntf->info.tab.propIndex);
  845.         if (hr != hrSuccess)
  846.         {
  847.             TraceSz1("SMS: HrApplyOQNotifChanges: HrDeleteRow returns sc == %s",
  848.                 SzDecodeScode(GetScode(hr)));
  849.             goto exit;
  850.         }
  851.         break;
  852.     case TABLE_ROW_ADDED:
  853.         /* add the row to the table. We don't care where in the table */
  854.         /* it goes, because the row will be sorted by the spooler in */
  855.         /* its view anyway. */
  856.         hr = lptbl->lpVtbl->HrModifyRow(lptbl, &lpntf->info.tab.row);
  857.         if (hr != hrSuccess)
  858.         {
  859.             TraceSz1("SMS: HrApplyOQNotifChanges: HrModifyRow returns sc == %s",
  860.                 SzDecodeScode(GetScode(hr)));
  861.             goto exit;
  862.         }
  863.         break;
  864.     default:
  865.         /* We don't expect any other table events than the */
  866.         /* two above. */
  867.         TraceSz1("SMS: HrApplyOQNotifChanges: unexpected ulTableEvent %08lX",
  868.             lpntf->info.tab.ulTableEvent);
  869.         break;
  870.     }
  871. exit:
  872.     FreeNull(ponb);
  873.     DebugTraceResult(HrApplyOQNotifChanges, hr);
  874.     return hr;
  875. }
  876. /*
  877.  * HrGetOutgoingNotificationKey
  878.  *
  879.  * Purpose  return the nofication key for the outgoing queue
  880.  *          memory should be freed with FreeNull
  881.  *          The key we use contains the full pathname to the outgoing
  882.  *          queue file on disk. This should be unique for the store
  883.  *          that we're running against.
  884.  *
  885.  * Parameters
  886.  * pims         the store whose outgoing queue is being referred to
  887.  * lppKey       pointer to the key
  888.  */
  889. static HRESULT HrGetOutgoingNotificationKey(PIMS pims, LPNOTIFKEY * lppKey)
  890. {
  891.     HRESULT hr = hrSuccess;
  892.     LPNOTIFKEY lpKey = NULL;
  893.     ULONG cb;                   /* number of bytes in the key */
  894.     LPSTR szPath = NULL;        /* path to outgoing queue */
  895.     hr = HrGetTableName((POBJ) pims, NULL, szOutgoingFileName, &szPath);
  896.     if (HR_FAILED(hr))
  897.         goto exit;
  898.     /* allocate space for the key */
  899.     cb = Cbtszsize(szPath);
  900.     hr = HrAlloc(CbNewNOTIFKEY(cb), (PPV) &lpKey);
  901.     if (hr != hrSuccess)
  902.         goto exit;
  903.     lstrcpy(lpKey->ab, szPath);
  904.     lpKey->cb = cb;
  905. exit:
  906.     FreeNull(szPath);
  907.     if (HR_FAILED(hr))
  908.     {
  909.         FreeNull(lpKey);
  910.         lpKey = NULL;
  911.     }
  912.     *lppKey = lpKey;
  913.     return hr;
  914. }
  915. /*
  916.  * HrNotifyOnOutgoingQueue
  917.  *
  918.  * Purpose
  919.  *  Send out a notification that the Outgoing Queue has had a row added
  920.  *  or deleted. Also send the filetime of the queue file before and after
  921.  *  the modification.
  922.  *
  923.  * Parameters
  924.  *  pims: A pointer to the message store object.
  925.  *  peid: (For TABLE_ROW_DELETED) The entryid of the message in the
  926.  *          queue that was deleted.
  927.  *  prw: (For TABLE_ROW_ADDED) A pointer to the row of data added
  928.  *          to the OG Queue.
  929.  *  ulTableEvent: Either TABLE_ROW_ADDED or TABLE_ROW_DELETED.
  930.  *  pftBeforeUpdate: A pointer to the filetime of the queue file before the
  931.  *          update was performed.
  932.  */
  933. static HRESULT HrNotifyOnOutgoingQueue(PIMS pims, PEID peid, LPSRow prw,
  934.     ULONG ulTableEvent, FILETIME *pftBeforeUpdate)
  935. {
  936.     HRESULT hr;
  937.     LPNOTIFKEY lpKey = NULL;
  938.     ULONG ulFlags = 0;
  939.     NOTIFICATION ntf;
  940.     NOTIFICATION ntfTemp;
  941.     PONB ponb = NULL;
  942.     ULONG cbNtf;
  943.     ULONG cbOut;
  944.     SCODE sc;
  945.     /* get the key */
  946.     hr = HrGetOutgoingNotificationKey(pims, &lpKey);
  947.     if (HR_FAILED(hr))
  948.         goto exit;
  949.     /* Assemble the notification. */
  950.     ntfTemp.ulEventType = fnevTableModified;
  951.     ntfTemp.info.tab.ulTableEvent = ulTableEvent;
  952.     ntfTemp.info.tab.hResult = hrSuccess;
  953.     if (ulTableEvent == TABLE_ROW_DELETED)
  954.     {
  955.         /* Send across the index property for the row: PR_INSTANCE_KEY */
  956.         ntfTemp.info.tab.propIndex.ulPropTag = PR_INSTANCE_KEY;
  957.         ntfTemp.info.tab.propIndex.Value.bin.cb = CbEID(peid);
  958.         ntfTemp.info.tab.propIndex.Value.bin.lpb = (BYTE *) peid;
  959.         memset(&(ntfTemp.info.tab.propPrior), 0, sizeof(SPropValue));
  960.         ntfTemp.info.tab.row.cValues = 0;
  961.         ntfTemp.info.tab.row.lpProps = NULL;
  962.     }
  963.     else
  964.     {
  965.         AssertSz(ulTableEvent == TABLE_ROW_ADDED,
  966.             "Bad event type: about to send bogus internal notification");
  967.         memset(&(ntfTemp.info.tab.propIndex), 0, sizeof(SPropValue));
  968.         memset(&(ntfTemp.info.tab.propPrior), 0, sizeof(SPropValue));
  969.         ntfTemp.info.tab.row = *prw;
  970.     }
  971.     sc = ScCountNotifications(1, &ntfTemp, &cbNtf);
  972.     if (sc != S_OK)
  973.     {
  974.         hr = ResultFromScode(sc);
  975.         goto exit;
  976.     }
  977.     hr = HrAlloc(CbNewONB(cbNtf), &ponb);
  978.     if (hr != hrSuccess)
  979.         goto exit;
  980.     ponb->cbNtf = cbNtf;
  981.     sc = ScCopyNotifications(1, &ntfTemp, (LPVOID) ponb->abNtf, &cbOut);
  982.     if (sc != S_OK)
  983.     {
  984.         hr = ResultFromScode(sc);
  985.         goto exit;
  986.     }
  987.     AssertSz(cbOut == cbNtf, "ScCopyNotifications used a different # of bytes "
  988.         "than ScCountNotifications returned.");
  989.     ponb->ftBeforeUpdate = *pftBeforeUpdate;
  990.     ponb->ftAfterUpdate = pims->ftOGQueue;
  991.     /* Pass across the notification's memory offset so that the receiving */
  992.     /* process can relocate the notification to its address space. */
  993.     ponb->pvRef = (LPVOID) ponb->abNtf;
  994.     
  995.     ntf.ulEventType = fnevExtended;
  996.     ntf.info.ext.ulEvent = 0;
  997.     ntf.info.ext.cb = CbONB(ponb);
  998.     ntf.info.ext.pbEventParameters = (LPBYTE) ponb;
  999.     hr = pims->psup->lpVtbl->Notify(pims->psup, lpKey, 1, &ntf, &ulFlags);
  1000. exit:
  1001.     FreeNull(lpKey);
  1002.     FreeNull(ponb);
  1003.     DebugTraceResult(HrNotifyOnOutgoingQueue, hr);
  1004.     return hr;
  1005. }