MSPNTFY.C
资源名称:MSDN_VC98.zip [点击查看]
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:38k
源码类别:
Windows编程
开发平台:
Visual C++
- /*
- * M S P N T F Y . C
- *
- * Code for doing internal cross-process notifications within the
- * Sample Message Store provider.
- *
- * Copyright 1992-1995 Microsoft Corporation. All Rights Reserved.
- */
- #include "msp.h"
- #include <stdarg.h>
- /* INTERNAL Function prototypes. */
- long STDAPICALLTYPE LSMSOQNotifCallback(LPVOID lpvContext, ULONG cNotif,
- LPNOTIFICATION lpNotifs);
- static void EmptyTable(LPTABLEDATA lptbl, PLMR plmr);
- static HRESULT HrApplyOQNotifChanges(LPTABLEDATA lptbl, PONB ponbIn);
- static HRESULT HrGetOutgoingNotificationKey(PIMS pims, LPNOTIFKEY * lppKey);
- static HRESULT HrNotifyOnOutgoingQueue(PIMS pims, PEID peid, LPSRow prw,
- ULONG ulTableEvent, FILETIME *pftBeforeUpdate);
- long STDAPICALLTYPE LSMSTblNotifCallback(LPVOID lpvContext, ULONG cNotif,
- LPNOTIFICATION lpNotifs);
- HRESULT HrApplyTblNotifChanges(PIMS pims, PTNB ptnbIn);
- static HRESULT HrGetTableNotificationKey(PIMS pims, LPNOTIFKEY * lppKey);
- /*
- * EXTERNAL FUNCTIONS (called from outside this file).
- */
- /*
- * HrCreateOGQueueMutex
- *
- * Purpose
- * Create the outgoing queue mutex, and return it to the caller.
- *
- * Arguments
- * phQMutex: Pointer to the location to return the new mutex.
- *
- * Returns:
- * HRESULT: Will return an error only if the CreateMutex call fails.
- */
- HRESULT HrCreateOGQueueMutex(HANDLE *phQMutex)
- {
- HRESULT hr = hrSuccess;
- HANDLE hMutex;
- LPTSTR szMutexName = "SMS_OGQUEUEFILE_MUTEX";
- hMutex = CreateMutex(NULL, FALSE, szMutexName);
- if (hMutex)
- *phQMutex = hMutex;
- #ifndef WIN16
- else
- {
- TraceSz1("SampleMS: HrCreateOGQueueMutex: call to"
- " CreateMutex failed (error %08lX)", GetLastError());
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- }
- #endif
- DebugTraceResult(HrCreateOGQueueMutex, hr);
- return hr;
- }
- /*
- * HrSetupPrivateNotifications
- *
- * Purpose
- * Setup two private channels via the MAPI notification engine to
- * tell other processes running against this store when 1) the
- * outgoing queue changes and 2) when contents and hierarchy tables
- * change. We communicate between the multiple client processes and
- * the one spooler process. For the outgoing queue, we use a key that
- * is the full pathname to the outgoing queue file. For the other
- * tables, we use a unique 16-byte ID. Remember the connections so
- * that we can Unsubscribe when we shutdown the store.
- *
- * Arguments
- * pims: a pointer to the message store object.
- *
- * Side Effects
- * Fills in the ulOQConn and ulTblConn members of pims.
- *
- * Returns
- * HRESULT
- */
- HRESULT HrSetupPrivateNotifications(PIMS pims)
- {
- HRESULT hr;
- LPMAPIADVISESINK lpAdvise;
- ULONG ulOQConn = 0;
- ULONG ulTblConn = 0;
- LPNOTIFKEY lpKey = NULL;
- /* Use the MAPI notification engine to tell myself */
- /* (across processes) when to update outgoing queue. */
- /* The key is the path to the disk cache of the outbound queue */
- hr = HrGetOutgoingNotificationKey(pims, &lpKey);
- if (hr != hrSuccess)
- goto exit;
- hr = HrAllocAdviseSink(&LSMSOQNotifCallback, (LPVOID) pims, &lpAdvise);
- if (hr != hrSuccess)
- goto exit;
- hr = pims->psup->lpVtbl->Subscribe(pims->psup, lpKey,
- fnevExtended, 0L, lpAdvise, &ulOQConn);
- /* Always release; mapi will have addref'ed it during Subscribe */
- UlRelease(lpAdvise);
- if (hr != hrSuccess)
- goto exit;
- FreeNull(lpKey);
- lpKey = NULL;
- /* Now, setup notifications for the other tables. */
- hr = HrGetTableNotificationKey(pims, &lpKey);
- if (hr != hrSuccess)
- goto exit;
- hr = HrAllocAdviseSink(&LSMSTblNotifCallback, (LPVOID) pims, &lpAdvise);
- if (hr != hrSuccess)
- goto exit;
- hr = pims->psup->lpVtbl->Subscribe(pims->psup, lpKey,
- fnevExtended, 0L, lpAdvise, &ulTblConn);
- /* Always release; mapi will have addref'ed it during Subscribe */
- UlRelease(lpAdvise);
- if (hr != hrSuccess)
- goto exit;
- exit:
- FreeNull(lpKey);
- if (hr == hrSuccess)
- {
- /* Remember our use of the notification engine. */
- pims->ulOQConn = ulOQConn;
- pims->ulTblConn = ulTblConn;
- }
- else if (ulOQConn != 0)
- (void) pims->psup->lpVtbl->Unsubscribe(pims->psup, ulOQConn);
- DebugTraceResult(HrSetupPrivateNotifications, hr);
- return hr;
- }
- /*
- * HrUpdateOutgoingQueue
- *
- * Purpose
- * Updates the outgoing queue based on the information given. If the
- * outgoing queue table is not open, or is out-of-date with respect to
- * the outgoing queue file on disk, initializes the table. The function
- * then applies the change requested to the table, writes the table on
- * disk, and notifies other processes of the change.
- *
- * Arguments
- * pims: A pointer to the message store object.
- * pimsg: For a TABLE_ROW_ADDED event, a pointer to the message being
- * added to the queue; otherwise, this parameter should be NULL.
- * peid: For a TABLE_ROW_DELETED event, a pointer to the entryid of the
- * message being deleted from the queue; otherwise, this parameter
- * should be NULL.
- * ulTableEvent: The type of update event: Either TABLE_ROW_ADDED or
- * TABLE_ROW_DELETED.
- *
- * Returns
- * HRESULT
- */
- HRESULT HrUpdateOutgoingQueue(PIMS pims, PIMSG pimsg, PEID peid,
- ULONG ulTableEvent)
- {
- HRESULT hr = hrSuccess;
- LPTABLEDATA lptbl;
- SRow srNewRow = {0, 0, NULL};
- LPSRow prw;
- BOOL fInMutex = FALSE;
- FILETIME ftBeforeUpdate;
- /* If the file mutex doesn't yet exist on this process, create it. */
- if (pims->hOGQueueMutex == NULL)
- {
- hr = HrCreateOGQueueMutex(&pims->hOGQueueMutex);
- if (hr != hrSuccess)
- goto exit;
- }
- /* Get the file mutex so that we can use the file (and change it) */
- /* without crossing paths with another process. */
- WaitForSingleObject(pims->hOGQueueMutex, INFINITE);
- fInMutex = TRUE;
- /* This routine will open the outgoing queue table if it's not already */
- /* open in this process, and will leave the opened copy around in pims. */
- if (pims->lptblOutgoing == NULL)
- {
- hr = HrNewOutgoingTableData(pims);
- if (hr != hrSuccess)
- goto exit;
- }
- lptbl = pims->lptblOutgoing;
- ftBeforeUpdate = pims->ftOGQueue;
- if (ulTableEvent == TABLE_ROW_ADDED)
- {
- ULONG cValues;
- AssertSz(pimsg, "A msg should be provided on an add");
- AssertSz(peid == NULL, "No entryid should be provided on an add");
- hr = pimsg->lpVtbl->GetProps(pimsg, (LPSPropTagArray) &sptaOutgoing,
- 0, /* ansi */
- &cValues, &srNewRow.lpProps);
- if (HR_FAILED(hr)) /* Ignore warnings from GetProps. */
- goto exit;
- srNewRow.cValues = cValues;
- hr = lptbl->lpVtbl->HrModifyRow(lptbl, &srNewRow);
- if (hr != hrSuccess)
- goto exit;
- prw = &srNewRow;
- }
- else
- {
- AssertSz(ulTableEvent == TABLE_ROW_DELETED,
- "Bad event type received");
- AssertSz(pimsg == NULL, "No msg should be provided on a delete");
- AssertSz(peid, "An entryid should be provided on a delete");
- /* remove it from the outgoing queue */
- hr = HrRemoveRow(lptbl, peid);
- if (hr != hrSuccess)
- goto exit;
- prw = NULL;
- }
- hr = HrWriteTableOnDisk(lptbl, (POBJ) pims, NULL, szOutgoingFileName);
- if (hr != hrSuccess)
- goto exit;
- /* Update the last mod time of the table inside this process's */
- /* message store object. */
- hr = HrGetFileModTime(pims->szStorePath, szOutgoingFileName,
- &pims->ftOGQueue);
- if (hr != hrSuccess)
- goto exit;
- hr = HrNotifyOnOutgoingQueue(pims, peid, prw, ulTableEvent,
- &ftBeforeUpdate);
- exit:
- if (fInMutex)
- ReleaseMutex(pims->hOGQueueMutex);
- LMFree(&pims->lmr, srNewRow.lpProps);
- DebugTraceResult(HrUpdateOutgoingQueue, hr);
- return hr;
- }
- /*
- * HrNewOutgoingTableData
- *
- * Purpose Checks the outgoing table data object in the message store.
- * If there isn't one, creates it and initializes it from disk.
- * If there is one, empties it, and re-initializes it from disk.
- * Should be inside the outgoing queue mutex during this function.
- *
- * Parameters
- * pims A pointer to the message store object.
- *
- * Side Effects
- * Fills in the lptblOutgoing member of the pims.
- * Updates pims->ftOGQueue with the last mod time of the
- * outgoing queue file.
- */
- HRESULT HrNewOutgoingTableData(PIMS pims)
- {
- HRESULT hr = hrSuccess;
- LPTABLEDATA lptbl = pims->lptblOutgoing;
- BOOL fTableCreated = FALSE;
- if (!lptbl)
- {
- PINST pinst;
- SCODE sc = S_OK;
- /* The table doesn't exist. Create it. */
- pinst = (PINST) PvGetInstanceGlobals();
- if (pinst == NULL)
- {
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- goto exit;
- }
- sc = CreateTable((LPIID) &IID_IMAPITableData, pims->lmr.lpAllocBuf,
- pims->lmr.lpAllocMore, pims->lmr.lpFreeBuf, pinst->lpmalloc,
- TBLTYPE_DYNAMIC, PR_INSTANCE_KEY, (LPSPropTagArray) &sptaOutgoing,
- &lptbl);
- if (sc != S_OK)
- {
- hr = ResultFromScode(sc);
- goto exit;
- }
- fTableCreated = TRUE;
- }
- else
- {
- /* The table exists already. Delete all rows from it. */
- EmptyTable(lptbl, &pims->lmr);
- }
- /* read the table in from disk */
- /* outgoing queue tables can't be regenerated, so any error reading */
- /* the table is fatal. We lose all messages in the OG queue if this */
- /* function has an error. */
- hr = HrReadTableFromDisk(lptbl, (POBJ) pims, NULL, OUTGOING_COLUMNS,
- szOutgoingFileName);
- if (hr != hrSuccess)
- {
- TraceSz("SMS: Bad OG Queue data on disk.");
- goto exit;
- }
- /* Verify that all messages in the table actually exist on disk. If */
- /* not, remove the row(s) and re-write the table to disk. */
- hr = HrSyncOutgoingTable(lptbl, pims);
- if (hr != hrSuccess)
- goto exit;
- /* Save away the last mod time of the table inside this process's */
- /* message store object. */
- hr = HrGetFileModTime(pims->szStorePath, szOutgoingFileName,
- &pims->ftOGQueue);
- if (hr != hrSuccess)
- goto exit;
- pims->lptblOutgoing = lptbl;
- exit:
- if (hr != hrSuccess && fTableCreated)
- UlRelease(lptbl);
- DebugTraceResult(HrNewOutgoingTableData, hr);
- return hr;
- }
- /*
- * INTERNAL Functions (called from within this file ONLY).
- *
- */
- /*----------------------------------------------------------------------+
- | |
- | CONTENTS AND HIERARCHY TABLE NOTIFICATION HANDLING |
- | |
- +----------------------------------------------------------------------*/
- /*
- * LSMSTblNotifCallback
- *
- * Purpose
- * Update the contents or hierarchy table associated with the folder eid
- * passed across. We should receive a notification when the tabledata
- * object needs to have a row added, deleted or modified. Calls
- * ChangeTable after decoding the notification.
- *
- * Arguments
- * lpvContext: A pointer to the message store object to use. We need
- * to verify that the object is still valid before using it.
- * cNotif: The number of notifications to process.
- * lpNotif: A pointer to an array of NOTIFICATION structures.
- *
- * Returns
- * LONG: Always returns 0.
- */
- long STDAPICALLTYPE LSMSTblNotifCallback(LPVOID lpvContext, ULONG cNotif,
- LPNOTIFICATION lpNotif)
- {
- PIMS pims = (PIMS) lpvContext;
- SCODE sc = S_OK;
- HRESULT hr = hrSuccess;
- PTNB ptnb;
- /*
- * Our code sends one extended notification at a time.
- * The notification consists of the table event that occurred along with
- * an object notification containing the entryids we need. We only use
- * two of the entryids in the object notification. The ParentID fields
- * refer to the parent folder of the table we need to update. The EntryID
- * fields refer to the object that changed within the folder. The
- * ulObjType field will be either MAPI_MESSAGE (for contents table
- * changes) or MAPI_FOLDER (for hierarchy table changes). All other
- * fields in the structure are unused and should be set to 0.
- */
- if (IMS_IsInvalid(pims)
- || cNotif != 1
- || (IsBadReadPtr(lpNotif, ((UINT) cNotif) * sizeof(NOTIFICATION)))
- || lpNotif->ulEventType != fnevExtended
- || lpNotif->info.ext.ulEvent != 0)
- return 0;
- ptnb = (PTNB) lpNotif->info.ext.pbEventParameters;
- if (IsBadReadPtr(ptnb, CbNewTNB(0))
- || IsBadReadPtr(ptnb, CbTNB(ptnb)))
- return 0;
- IMS_EnterCriticalSection(pims);
- hr = HrApplyTblNotifChanges(pims, ptnb);
- IMS_LeaveCriticalSection(pims);
- if (hr != hrSuccess)
- sc = GetScode(hr);
- return sc;
- }
- /*
- * HrApplyTblNotifChanges
- *
- * Purpose
- * This function relocates and validates the internal object notification
- * passed in, and then calls ChangeTable to actually update any open tables
- * within this process with the change given. The notification needs to be
- * relocated because the pointers from the other process may not be valid
- * on this process.
- *
- * Parameters
- * pims: A pointer to the message store object.
- * ptnbIn: A pointer to the table notification block (TNB) containing the
- * data we need in order to update any open tables om this process.
- *
- * Returns: validation errors or hrSuccess.
- */
- HRESULT HrApplyTblNotifChanges(PIMS pims, PTNB ptnbIn)
- {
- HRESULT hr = hrSuccess;
- SCODE sc = S_OK;
- LPNOTIFICATION lpntf;
- PTNB ptnb = NULL;
- OBJECT_NOTIFICATION *pon;
- ULONG cb;
- /* Allocate a new notification block, and copy the data over before */
- /* relocation. */
- hr = HrAlloc(CbNewTNB(ptnbIn->cbNtf), &ptnb);
- if (hr != hrSuccess)
- goto ret;
- memcpy(ptnb, ptnbIn, (UINT) CbNewTNB(ptnbIn->cbNtf));
- lpntf = (LPNOTIFICATION) ptnb->abNtf;
- if (lpntf->ulEventType != fnevObjectModified)
- {
- TraceSz1("SMS: HrApplyTblNotifChanges: Bad ulEventType %08lX "
- "received", lpntf->ulEventType);
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- goto ret;
- }
- /* Relocate the notification into the address space of this process. */
- /* We passed along the memory offset of the originating process to allow */
- /* this code to work. Note that ScRelocNotifications currently only */
- /* works from "bad" addresses to "good" addresses. The code assumes that */
- /* pointers are "bad" inside the notification, and that after conversion, */
- /* they are valid. */
- sc = ScRelocNotifications(1, lpntf, ptnb->pvRef, (LPVOID) lpntf, &cb);
- if (sc != S_OK)
- {
- hr = ResultFromScode(sc);
- goto ret;
- }
- if (ptnb->cbNtf != cb)
- {
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- goto ret;
- }
- pon = (OBJECT_NOTIFICATION *) &(lpntf->info.obj);
- if (pon->ulObjType != MAPI_MESSAGE && pon->ulObjType != MAPI_FOLDER)
- {
- TraceSz1("SMS: HrApplyTblNotifChanges: unexpected Object Type %08lX",
- pon->ulObjType);
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- goto ret;
- }
- if ( FIsInvalidEID(pon->cbParentID, (PEID) pon->lpParentID, pims)
- || !FIsFolder((PEID) pon->lpParentID))
- {
- TraceSz("SMS: HrApplyTblNotifChanges: invalid parent entryid");
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- goto ret;
- }
- /* TABLE_CHANGED events don't require a lpEntryID, because multiple
- * objects have changed. ChangeTable() simply validates all rows in
- * the table against the files on disk.
- */
- if ( ptnb->ulTableEvent != TABLE_CHANGED
- && FIsInvalidEID(pon->cbEntryID, (PEID) pon->lpEntryID, pims))
- {
- TraceSz("SMS: HrApplyTblNotifChanges: invalid entryid");
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- goto ret;
- }
- ChangeTable(pims, (PEID) pon->lpParentID, (PEID) pon->lpEntryID,
- pon->ulObjType, ptnb->ulTableEvent, FALSE);
- ret:
- FreeNull(ptnb);
- DebugTraceResult(HrApplyTblNotifChanges, hr);
- return hr;
- }
- /*
- * HrSendNotif
- *
- * Purpose
- * This function constructs and sends a notification to other active Sample
- * Message Store processes open on this message store file. The notification
- * describes a change (add, delete, or modify) to either a message (contents
- * table) or folder (hierarchy table). The receiver will need to get two
- * entryids, one for the parent folder of the changed object, and one for
- * the changed object itself. The receiver also needs to know whether the
- * changed object is a message or a folder, and what type of change occurred:
- * add, delete, modify, or change (contents tables only).
- *
- * Parameters
- * pims: Pointer to the message store object.
- * peidParent: The entryid of the folder containing the table to update and
- * the object that changed.
- * peidObject: The entryid of the object that changed. May be NULL when
- * sending a TABLE_CHANGED notification (contents tables only).
- * ulTableEvent: TABLE_ROW_ADDED, TABLE_ROW_DELETED, TABLE_ROW_MODIFIED, or
- * TABLE_CHANGED (TABLE_CHANGED only works on contents tables).
- * ulObjType: The type of object that changed. This implies the type of table
- * to update. May be MAPI_MESSAGE (contents table) or MAPI_FOLDER
- * (hierarchy table).
- *
- * Returns: Memory and disk errors or success.
- */
- HRESULT HrSendNotif(PIMS pims, PEID peidParent, PEID peidObject,
- ULONG ulTableEvent, ULONG ulObjType)
- {
- HRESULT hr = hrSuccess;
- SCODE sc;
- LPNOTIFKEY lpKey = NULL;
- NOTIFICATION ntfTemp;
- NOTIFICATION ntf;
- PTNB ptnb = NULL;
- ULONG cbNtf;
- ULONG cbOut;
- ULONG ulFlags = 0;
- /* Get the key */
- hr = HrGetTableNotificationKey(pims, &lpKey);
- if (hr != hrSuccess)
- goto ret;
- /*
- * Our code sends one extended notification at a time. The notification
- * consists of the table event that occurred along with an object
- * notification containing the entryids we need. We send the type of
- * table event as part of our extended notification structure, and
- * package the object notification always as "fnevObjectModified". We
- * only use two of the entryids in the object notification. The ParentID
- * fields refer to the parent folder of the table we need to update. The
- * EntryID fields refer to the object that changed within the folder. The
- * ulObjType field will be either MAPI_MESSAGE (for contents table
- * changes) or MAPI_FOLDER (for hierarchy table changes). All other
- * fields in the structure are unused and should be set to 0.
- */
- memset(&ntfTemp, 0, sizeof(NOTIFICATION));
- /* We always send the same type of event here. This is enough to get the
- * notification code to count and relocate what we send. We send the real
- * table event in ptnb->ulTableEvent (see below).
- */
- ntfTemp.ulEventType = fnevObjectModified;
- ntfTemp.info.obj.ulObjType = ulObjType;
- ntfTemp.info.obj.lpParentID = (LPENTRYID) peidParent;
- ntfTemp.info.obj.cbParentID = CbEID(peidParent);
- if (ulTableEvent != TABLE_CHANGED)
- {
- ntfTemp.info.obj.lpEntryID = (LPENTRYID) peidObject;
- ntfTemp.info.obj.cbEntryID = CbEID(peidObject);
- }
- sc = ScCountNotifications(1, &ntfTemp, &cbNtf);
- if (sc != S_OK)
- {
- hr = ResultFromScode(sc);
- goto ret;
- }
- hr = HrAlloc(CbNewTNB(cbNtf), &ptnb);
- if (hr != hrSuccess)
- goto ret;
- /* Here's where we send the table event. It's either TABLE_ROW_ADDED,
- * TABLE_ROW_DELETED, TABLE_ROW_MODIFIED, or TABLE_CHANGED.
- */
- ptnb->ulTableEvent = ulTableEvent;
- ptnb->cbNtf = cbNtf;
- sc = ScCopyNotifications(1, &ntfTemp, (LPVOID) ptnb->abNtf, &cbOut);
- if (sc != S_OK)
- {
- hr = ResultFromScode(sc);
- goto ret;
- }
- AssertSz(cbOut == cbNtf, "ScCopyNotifications used a different # of bytes "
- "than ScCountNotifications returned.");
- /* Pass across the notification's memory offset so that the receiving */
- /* process can relocate the notification to its address space. */
- ptnb->pvRef = (LPVOID) ptnb->abNtf;
- ntf.ulEventType = fnevExtended;
- ntf.info.ext.ulEvent = 0;
- ntf.info.ext.cb = CbTNB(ptnb);
- ntf.info.ext.pbEventParameters = (LPBYTE) ptnb;
- hr = pims->psup->lpVtbl->Notify(pims->psup, lpKey, 1, &ntf, &ulFlags);
- ret:
- FreeNull(lpKey);
- FreeNull(ptnb);
- DebugTraceResult(HrSendNotif, hr);
- return hr;
- }
- /*
- * HrGetTableNotificationKey
- *
- * Purpose
- * Generate and return the notification key that will allow cross-process
- * notifications for changes to any contents or hierarchy tables within a
- * store. The memory returned should be freed with FreeNull. This
- * notification key needs to work for processes attached to this particular
- * message store, and should only receive notifications having to do with
- * changes to tables. The key contains the store guid (unique for this store)
- * preceeded by a ULONG with 0x0000ABCD in it. Note that the choice of
- * 0x0000ABCD is arbitrary. As long as the sender sends to the same key as
- * the receiver listens to, and no unexpected sender sends to that key,
- * we're fine.
- *
- * Parameters
- * pims pointer to the message store object.
- * lppKey pointer to the location to return the new key.
- */
- static HRESULT HrGetTableNotificationKey(PIMS pims, LPNOTIFKEY * lppKey)
- {
- HRESULT hr = hrSuccess;
- LPNOTIFKEY lpKey = NULL;
- ULONG cb; /* number of bytes in the key */
- /* allocate space for the key */
- cb = sizeof(ULONG) + sizeof(MAPIUID);
- hr = HrAlloc(CbNewNOTIFKEY(cb), (PPV) &lpKey);
- if (hr != hrSuccess)
- goto exit;
- *((ULONG *) &(lpKey->ab[0])) = 0x0000ABCD;
- GetResourceUID(pims, (MAPIUID *) &(lpKey->ab[sizeof(ULONG)]));
- lpKey->cb = cb;
- exit:
- if (HR_FAILED(hr))
- {
- FreeNull(lpKey);
- lpKey = NULL;
- }
- *lppKey = lpKey;
- DebugTraceResult(HrGetTableNotificationKey, hr);
- return hr;
- }
- /*----------------------------------------------------------------------+
- | |
- | OUTGOING QUEUE NOTIFICATION HANDLING |
- | |
- +----------------------------------------------------------------------*/
- /*
- * LSMSOQNotifCallback
- *
- * Purpose
- * Update the outgoing queue table associated with the process that
- * the spooler is using. We should receive a notification when the
- * tabledata object needs to have a row added or deleted. If the
- * tabledata object exists on the message store object, then call
- * HrModifyRow (when a row is added), or HrRemoveRow (when a row is
- * deleted) to update the table appropriately.
- *
- * Arguments
- * lpvContext: A pointer to the message store object to use. We need
- * to verify that the object is still valid before using it.
- * cNotif: The number of notifications to process.
- * lpNotif: A pointer to an array of NOTIFICATION structures.
- *
- * Returns
- * LONG: Always returns 0.
- */
- long STDAPICALLTYPE LSMSOQNotifCallback(LPVOID lpvContext, ULONG cNotif,
- LPNOTIFICATION lpNotif)
- {
- PIMS pims = (PIMS) lpvContext;
- SCODE sc = S_OK;
- HRESULT hr = hrSuccess;
- FILETIME ftCurrent;
- BOOL fInMutex = FALSE;
- PONB ponb;
- /* Our code sends one extended notification at a time. This */
- /* extended notification contains two filetimes (the time */
- /* before the outgoing queue file was modified, and the time after */
- /* the change was made), and a standard notification with the change */
- /* to apply to the table. It should be an fnevTableModified, and */
- /* should be either TABLE_ROW_ADDED or TABLE_ROW_DELETED. If we ever */
- /* receive anything other than this, the code must change. */
- if (IMS_IsInvalid(pims)
- || cNotif != 1
- || (IsBadReadPtr(lpNotif, ((UINT) cNotif) * sizeof(NOTIFICATION)))
- || lpNotif->ulEventType != fnevExtended
- || lpNotif->info.ext.ulEvent != 0)
- return 0;
- ponb = (PONB) lpNotif->info.ext.pbEventParameters;
- if (IsBadReadPtr(ponb, CbNewONB(0))
- || IsBadReadPtr(ponb, CbONB(ponb)))
- return 0;
- IMS_EnterCriticalSection(pims);
- /* Check to see if the outgoing queue table data is open. If it isn't, */
- /* there is nothing to do, because the current table will be read and */
- /* initialized from disk when the spooler opens it. We're done. */
- if (!pims->lptblOutgoing)
- goto exit;
- /* If the file mutex doesn't yet exist on this process, create it. */
- if (pims->hOGQueueMutex == NULL)
- {
- hr = HrCreateOGQueueMutex(&pims->hOGQueueMutex);
- if (hr != hrSuccess)
- goto exit;
- }
- /* Get the file mutex so that we can use the file (and change it) */
- /* without crossing paths with another process. */
- WaitForSingleObject(pims->hOGQueueMutex, INFINITE);
- fInMutex = TRUE;
- /* Get time that the file was last modified */
- hr = HrGetFileModTime(pims->szStorePath, szOutgoingFileName, &ftCurrent);
- if (hr != hrSuccess)
- goto exit;
- /* If the time that this process last read the file is the same as */
- /* the time that the file was last modified, then don't do anything */
- /* because we already have all changes, including the one sent to us */
- /* in this notification. This can happen because we picked up two */
- /* changes at once, or because this process is actually the same */
- /* process that sent the notification. */
- if (CompareFileTime(&ftCurrent, &pims->ftOGQueue) == 0)
- goto exit;
- /* If the time that this process last read the file is the same as */
- /* the time that the other process read the file, then we can simply */
- /* apply the changes sent in the notification itself, and update our */
- /* time to the time after update sent in the notification. */
- /* If the times are different, or there is a problem applying the */
- /* changes, then reconstruct the table from the on-disk copy. */
- if ((CompareFileTime(&ponb->ftBeforeUpdate, &pims->ftOGQueue) == 0)
- && (HrApplyOQNotifChanges(pims->lptblOutgoing, ponb) == hrSuccess))
- {
- pims->ftOGQueue = ponb->ftAfterUpdate;
- }
- else
- {
- hr = HrNewOutgoingTableData(pims);
- if (hr != hrSuccess)
- goto exit;
- }
- exit:
- if (fInMutex)
- ReleaseMutex(pims->hOGQueueMutex);
- IMS_LeaveCriticalSection(pims);
- if (hr != hrSuccess)
- sc = GetScode(hr);
- return sc;
- }
- /*
- * EmptyTable
- *
- * Purpose
- * Deletes all rows from the table data object given.
- * Helper function for the outgoing queue table notification callback
- * routine.
- *
- * Arguments
- * lptbl: A pointer to the tabledata object to empty.
- * plmr: A pointer to the linked memory routines.
- *
- * Returns
- * void.
- */
- static void EmptyTable(LPTABLEDATA lptbl, PLMR plmr)
- {
- HRESULT hr;
- LPSRow lpsRow;
- LPSPropValue pval;
- LPSPropValue pvalMac;
- while (TRUE)
- {
- /* Get the first row. Note that as we delete rows, this will */
- /* keep giving us a new row. */
- hr = lptbl->lpVtbl->HrEnumRow(lptbl, 0, &lpsRow);
- if (hr != hrSuccess)
- {
- TraceSz1("Sample MS: EmptyTable: HrEnumRow failed with"
- " sc == %s", SzDecodeScode(GetScode(hr)));
- break;
- }
- /* The table is empty when no row is returned */
- if (!lpsRow)
- break;
- /* find the entryid in the property value array */
- pval = lpsRow->lpProps;
- pvalMac = pval + lpsRow->cValues;
- for (; pval < pvalMac; ++pval)
- if (pval->ulPropTag == PR_INSTANCE_KEY)
- break;
- /* Every row should contain an inst key. It is the index property. */
- if (pval == pvalMac)
- {
- TrapSz("No PR_INSTANCE_KEY found in the table row");
- break;
- }
- /* delete this row from the table */
- hr = lptbl->lpVtbl->HrDeleteRow(lptbl, pval);
- LMFree(plmr, lpsRow);
- lpsRow = NULL;
- if (hr != hrSuccess)
- {
- TraceSz1("Sample MS: EmptyTable: HrDeleteRow failed with"
- " error %s", SzDecodeScode(GetScode(hr)));
- break;
- }
- }
- return;
- }
- /*
- * HrApplyOQNotifChanges
- *
- * Purpose
- * Helper function of the notification callback routine. If the
- * table on disk hasn't changed except for the notification given,
- * then we can update the outgoing queue table data directly instead
- * of re-reading the table from disk. This function modifies the
- * table data directly by calling HrModifyRow (when a row is added),
- * or HrRemoveRow (when a row is deleted).
- * Note that we have to convert the notification inside the ONB so
- * that the pointers are valid. Also note that we may NOT modify the
- * notification data at all; therefore, we must copy the data before
- * changing it.
- *
- * Arguments
- * lptbl: a pointer to the table data object to update.
- * ponbIn: a pointer to the ONB received in the callback.
- *
- * Returns
- * HRESULT
- */
- static HRESULT HrApplyOQNotifChanges(LPTABLEDATA lptbl, PONB ponbIn)
- {
- HRESULT hr = hrSuccess;
- SCODE sc;
- ULONG cb;
- LPNOTIFICATION lpntf;
- PONB ponb = NULL;
- /* Allocate a new notification block, and copy the data over before */
- /* relocation. */
- hr = HrAlloc(CbNewONB(ponbIn->cbNtf), &ponb);
- if (hr != hrSuccess)
- goto exit;
- memcpy(ponb, ponbIn, (UINT) CbNewONB(ponbIn->cbNtf));
- lpntf = (LPNOTIFICATION) ponb->abNtf;
- /* Relocate the notification into the address space of this process. */
- /* We passed along the memory offset of the originating process to allow */
- /* this code to work. Note that ScRelocNotifications currently only */
- /* works from "bad" addresses to "good" addresses. The code assumes that */
- /* pointers are "bad" inside the notification, and that after conversion, */
- /* they are valid. */
- sc = ScRelocNotifications(1, lpntf, ponb->pvRef, (LPVOID) lpntf, &cb);
- if (sc != S_OK)
- {
- hr = ResultFromScode(sc);
- goto exit;
- }
- if (ponb->cbNtf != cb)
- {
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- goto exit;
- }
- /* We don't expect any events other than those for a table. */
- if (lpntf->ulEventType != fnevTableModified)
- {
- TraceSz1("SMS: HrApplyOQNotifChanges: unexpected ulEventType %08lX",
- lpntf->ulEventType);
- hr = ResultFromScode(MAPI_E_CALL_FAILED);
- goto exit;
- }
- switch (lpntf->info.tab.ulTableEvent)
- {
- case TABLE_ROW_DELETED:
- /* delete the row from the table according to the index */
- /* property value in the notification structure */
- hr = lptbl->lpVtbl->HrDeleteRow(lptbl, &lpntf->info.tab.propIndex);
- if (hr != hrSuccess)
- {
- TraceSz1("SMS: HrApplyOQNotifChanges: HrDeleteRow returns sc == %s",
- SzDecodeScode(GetScode(hr)));
- goto exit;
- }
- break;
- case TABLE_ROW_ADDED:
- /* add the row to the table. We don't care where in the table */
- /* it goes, because the row will be sorted by the spooler in */
- /* its view anyway. */
- hr = lptbl->lpVtbl->HrModifyRow(lptbl, &lpntf->info.tab.row);
- if (hr != hrSuccess)
- {
- TraceSz1("SMS: HrApplyOQNotifChanges: HrModifyRow returns sc == %s",
- SzDecodeScode(GetScode(hr)));
- goto exit;
- }
- break;
- default:
- /* We don't expect any other table events than the */
- /* two above. */
- TraceSz1("SMS: HrApplyOQNotifChanges: unexpected ulTableEvent %08lX",
- lpntf->info.tab.ulTableEvent);
- break;
- }
- exit:
- FreeNull(ponb);
- DebugTraceResult(HrApplyOQNotifChanges, hr);
- return hr;
- }
- /*
- * HrGetOutgoingNotificationKey
- *
- * Purpose return the nofication key for the outgoing queue
- * memory should be freed with FreeNull
- * The key we use contains the full pathname to the outgoing
- * queue file on disk. This should be unique for the store
- * that we're running against.
- *
- * Parameters
- * pims the store whose outgoing queue is being referred to
- * lppKey pointer to the key
- */
- static HRESULT HrGetOutgoingNotificationKey(PIMS pims, LPNOTIFKEY * lppKey)
- {
- HRESULT hr = hrSuccess;
- LPNOTIFKEY lpKey = NULL;
- ULONG cb; /* number of bytes in the key */
- LPSTR szPath = NULL; /* path to outgoing queue */
- hr = HrGetTableName((POBJ) pims, NULL, szOutgoingFileName, &szPath);
- if (HR_FAILED(hr))
- goto exit;
- /* allocate space for the key */
- cb = Cbtszsize(szPath);
- hr = HrAlloc(CbNewNOTIFKEY(cb), (PPV) &lpKey);
- if (hr != hrSuccess)
- goto exit;
- lstrcpy(lpKey->ab, szPath);
- lpKey->cb = cb;
- exit:
- FreeNull(szPath);
- if (HR_FAILED(hr))
- {
- FreeNull(lpKey);
- lpKey = NULL;
- }
- *lppKey = lpKey;
- return hr;
- }
- /*
- * HrNotifyOnOutgoingQueue
- *
- * Purpose
- * Send out a notification that the Outgoing Queue has had a row added
- * or deleted. Also send the filetime of the queue file before and after
- * the modification.
- *
- * Parameters
- * pims: A pointer to the message store object.
- * peid: (For TABLE_ROW_DELETED) The entryid of the message in the
- * queue that was deleted.
- * prw: (For TABLE_ROW_ADDED) A pointer to the row of data added
- * to the OG Queue.
- * ulTableEvent: Either TABLE_ROW_ADDED or TABLE_ROW_DELETED.
- * pftBeforeUpdate: A pointer to the filetime of the queue file before the
- * update was performed.
- */
- static HRESULT HrNotifyOnOutgoingQueue(PIMS pims, PEID peid, LPSRow prw,
- ULONG ulTableEvent, FILETIME *pftBeforeUpdate)
- {
- HRESULT hr;
- LPNOTIFKEY lpKey = NULL;
- ULONG ulFlags = 0;
- NOTIFICATION ntf;
- NOTIFICATION ntfTemp;
- PONB ponb = NULL;
- ULONG cbNtf;
- ULONG cbOut;
- SCODE sc;
- /* get the key */
- hr = HrGetOutgoingNotificationKey(pims, &lpKey);
- if (HR_FAILED(hr))
- goto exit;
- /* Assemble the notification. */
- ntfTemp.ulEventType = fnevTableModified;
- ntfTemp.info.tab.ulTableEvent = ulTableEvent;
- ntfTemp.info.tab.hResult = hrSuccess;
- if (ulTableEvent == TABLE_ROW_DELETED)
- {
- /* Send across the index property for the row: PR_INSTANCE_KEY */
- ntfTemp.info.tab.propIndex.ulPropTag = PR_INSTANCE_KEY;
- ntfTemp.info.tab.propIndex.Value.bin.cb = CbEID(peid);
- ntfTemp.info.tab.propIndex.Value.bin.lpb = (BYTE *) peid;
- memset(&(ntfTemp.info.tab.propPrior), 0, sizeof(SPropValue));
- ntfTemp.info.tab.row.cValues = 0;
- ntfTemp.info.tab.row.lpProps = NULL;
- }
- else
- {
- AssertSz(ulTableEvent == TABLE_ROW_ADDED,
- "Bad event type: about to send bogus internal notification");
- memset(&(ntfTemp.info.tab.propIndex), 0, sizeof(SPropValue));
- memset(&(ntfTemp.info.tab.propPrior), 0, sizeof(SPropValue));
- ntfTemp.info.tab.row = *prw;
- }
- sc = ScCountNotifications(1, &ntfTemp, &cbNtf);
- if (sc != S_OK)
- {
- hr = ResultFromScode(sc);
- goto exit;
- }
- hr = HrAlloc(CbNewONB(cbNtf), &ponb);
- if (hr != hrSuccess)
- goto exit;
- ponb->cbNtf = cbNtf;
- sc = ScCopyNotifications(1, &ntfTemp, (LPVOID) ponb->abNtf, &cbOut);
- if (sc != S_OK)
- {
- hr = ResultFromScode(sc);
- goto exit;
- }
- AssertSz(cbOut == cbNtf, "ScCopyNotifications used a different # of bytes "
- "than ScCountNotifications returned.");
- ponb->ftBeforeUpdate = *pftBeforeUpdate;
- ponb->ftAfterUpdate = pims->ftOGQueue;
- /* Pass across the notification's memory offset so that the receiving */
- /* process can relocate the notification to its address space. */
- ponb->pvRef = (LPVOID) ponb->abNtf;
- ntf.ulEventType = fnevExtended;
- ntf.info.ext.ulEvent = 0;
- ntf.info.ext.cb = CbONB(ponb);
- ntf.info.ext.pbEventParameters = (LPBYTE) ponb;
- hr = pims->psup->lpVtbl->Notify(pims->psup, lpKey, 1, &ntf, &ulFlags);
- exit:
- FreeNull(lpKey);
- FreeNull(ponb);
- DebugTraceResult(HrNotifyOnOutgoingQueue, hr);
- return hr;
- }