/* * NOTIFY.C * * WAB Notification Engine * * Copyright 1996 Microsoft Corporation. All Rights Reserved. * * * Notification in the WAB works as follows: * Client apps call Advise to register their interest in particular * notifications. The wab maintains a local list of the notifications * that the client has advised in the processes heap. The wab also * maintains a thread while there are any active advise sessions. This * thread waits on the global notification event. * * When a notification event happens (through HrFireNotification) * the event is written into a shared memory list and the global * notification event is triggered. * * The client thread (one in each process) wakes when this event is * triggered and compares the shared memory list of events against * it's local list of advises. If a match is found, the thread calls * the advise's OnNotify callback. * * There is a reference count in the global notification list's records * so that the record can be cleaned up when all processes have had * a chance to see it. * */ #include "_apipch.h" #define ADVISE_TIMEOUT 60000 // milliseconds // #define NEW_STUFF #ifdef NEW_STUFF #define NOTIFY_CREATE_TIMEOUT 60000 // milliseconds #define FIRE_NOTIFY_TIMEOUT 10000 // milliseconds #define ADVISE_THREAD_TIMEOUT ((ULONG)-1) // Forever #define NOTIFY_ADVISE_TIMEOUT 60000 // milliseconds // Per-process globals // 0 1 2 // 012345678901234567890123 const TCHAR szNotificationName[] = "_MICROSOFT_WAB_NOTIFY_"; const TCHAR szMEM[] = "MEM"; // suffix for shared memory const TCHAR szEVT[] = "EVT"; // suffix for event const TCHAR szMTX[] = "MTX"; // suffix for mutex LPNOTIFICATION_LIST lpNotificationList = NULL; HANDLE hmemNotificationList = NULL; HANDLE hevNotificationList = NULL; HANDLE hmtxNotificationList = NULL; HANDLE hevNotificationUI = NULL; HANDLE hmtxAdviseList = NULL; ADVISE_LIST AdviseList = {0, NULL}; HANDLE hevKillAdvise = NULL; ULONG ulMaxIdentifierSeen = 0; // Forward declarations DWORD AdviseThread(LPDWORD lpdwParam); /*************************************************************************** Name : WaitForTwoObjects Purpose : Wait for one of two objects to be signalled Parameters: handle0 = first object handle handle1 = second object handle dwTimeout = timeout in milliseconds Returns : index of object or -1 on error (0, 1 or -1) Comment : ***************************************************************************/ ULONG WaitForTwoObjects(HANDLE handle0, HANDLE handle1, DWORD dwTimeout) { HANDLE rgHandles[2] = {handle0, handle1}; switch (WaitForMultipleObjects(2, rgHandles, FALSE, dwTimeout)) { case WAIT_ABANDONED_0: DebugTrace("WaitFoMultipleObjects got WAIT_ABANDONED_0\n"); case WAIT_OBJECT_0: return(0); case WAIT_ABANDONED_0 + 1: DebugTrace("WaitFoMultipleObjects got WAIT_ABANDONED_1\n"); case WAIT_OBJECT_0 + 1: return(1); case WAIT_FAILED: default: DebugTrace("WaitForMultipleObjects got WAIT_FAILED: %u\n", GetLastError()); case WAIT_TIMEOUT: return((ULONG)-1); } } /*************************************************************************** Name : CompareEntryIDs Purpose : Are the two entryID's the same? Parameters: cbEntryID1 = sizeof lpEntryID1 lpEntryID1 = first EntryID cbEntryID2 = sizeof lpEntryID2 lpEntryID2 = second EntryID Returns : TRUE if the entry IDs are the same Comment : ***************************************************************************/ BOOL CompareEntryIDs(ULONG cbEntryID1, LPENTRYID lpEntryID1, ULONG cbEntryID2, LPENTRYID lpEntryID2) { BOOL fReturn = FALSE; if (cbEntryID1 == cbEntryID2) { if (cbEntryID1 && 0 == memcmp((LPVOID)lpEntryID1, (LPVOID)lpEntryID2, (size_t)cbEntryID1)) { fReturn = TRUE; } } return(fReturn); } /*************************************************************************** Name : CreateNotifySession Purpose : Create/Open the notification lists and thread. Parameters: lpfExisted -> returned flag TRUE if the session was already setup for this process. Returns : HRESULT Comment : Fills in these globals: hmtxNotificationList hevNotificationList hmemNotificationList lpNotificationList ***************************************************************************/ HRESULT CreateNotifySession(LPBOOL lpfExisted) { HRESULT hResult = hrSuccess; BOOL fMutex = FALSE; DWORD dwThreadId; DWORD dwThreadParam = 0; HANDLE hthrdAdvise = NULL; TCHAR szName[CharSizeOf(szNotificationName) + CharSizeOf(szMEM)]; Assert(CharSizeOf(szMEM) == CharSizeOf(szEVT) && CharSizeOf(szEVT) == CharSizeOf(szMTX)); StrCpyN(szName, szNotificationName, ARRAYSIZE(szName)); StrCatBuff(szName, szMTX, ARRAYSIZE(szName)); if (! (hmtxNotificationList = CreateMutex(NULL, FALSE, szName))) { DebugTrace("CreateNotifySession:CreateMutex(%s) -> %u\n", szName, GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } if (hResult = HrWaitForObject(hmtxNotificationList, NOTIFY_CREATE_TIMEOUT)) { DebugTrace("CreateNotifySession:Mutex wait failed\n"); goto exit; } fMutex = TRUE; StrCpyN(szName, szNotificationName, ARRAYSIZE(szName)); StrCatBuff(szName, szMEM, ARRAYSIZE(szName)); if ((hmemNotificationList = CreateFileMapping(INVALID_HANDLE_VALUE, // handle NULL, // security descriptor PAGE_READWRITE, // reserve more 0, // max size high MAX_NOTIFICATION_SPACE, // max size low szName)) == NULL) { // name DebugTrace("CreateNotifySession: CreateFileMapping(%s) --> %u\n", szName, GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } *lpfExisted = (GetLastError() == ERROR_ALREADY_EXISTS); if ((lpNotificationList = (LPNOTIFICATION_LIST)MapViewOfFile(hmemNotificationList, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, sizeof(NOTIFICATION_LIST))) == NULL) { DebugTrace("CreateNotifySession: CreateFileMapping --> %u\n", GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } if (! *lpfExisted) { // Initialize global notification list lpNotificationList->cAdvises = 0; // Number of advise processes lpNotificationList->cEntries = 0; // Number of entries in the list lpNotificationList->lpNode = NULL; // First node in list or NULL if empty lpNotificationList->ulNextIdentifier = 1; // next value for a notification identifier } lpNotificationList->cAdvises++; // Number of advise processes // Notification Event StrCpyN(szName, szNotificationName, ARRAYSIZE(szName)); StrCatBUff(szName, szEVT, ARRAYSIZE(szName)); if (! (hevNotificationList = CreateEvent(NULL, TRUE, // Manual reset FALSE, // initial state (not triggered) szName))) { DebugTrace("CreateNotifySession:CreateEvent(%S) -> %u\n", szName, GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } // Advise Kill Event if (! (hevKillAdvise = CreateEvent(NULL, TRUE, // Manual reset FALSE, // initial state (not triggered) NULL))) { DebugTrace("CreateNotifySession:CreateEvent(Kill Advise) -> %u\n", GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } // Create the Local AdviseList if (! (hmtxAdviseList = CreateMutex(NULL, FALSE, // Not initially owned NULL))) { // no name DebugTrace("CreateNotifySession:CreateMutex(Advise List) -> %u\n", GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } // Local AdviseList should be empty Assert(AdviseList.cAdvises == 0); Assert(AdviseList.lpNode == NULL); // Create the Advise thread for this process if (! (hthrdAdvise = CreateThread(NULL, // no security attributes 0, // default stack size: BUGBUG: Should be smaller (LPTHREAD_START_ROUTINE)AdviseThread, // thread function &dwThreadParam, // argument to thread 0, // flags &dwThreadId))) { DebugTrace("CreateNotifySession:CreateThread -> %u\n", GetLastError()); // propbably out of memory? hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } exit: if (fMutex) { ReleaseMutex(hmtxNotificationList); } if (hthrdAdvise) { CloseHandle(hthrdAdvise); } if (hResult) { // Failure, clean up if (lpNotificationList) { UnmapViewOfFile(lpNotificationList); lpNotificationList = NULL; } if (hmemNotificationList) { CloseHandle(hmemNotificationList); hmemNotificationList = NULL; } if (hmtxNotificationList) { CloseHandle(hmtxNotificationList); hmtxNotificationList = NULL; } if (hevNotificationList) { CloseHandle(hevNotificationList); hevNotificationList = NULL; } if (hevKillAdvise) { CloseHandle(hevKillAdvise); hevKillAdvise = NULL; } } return(hResult); } /*************************************************************************** Name : OpenNotifySession Purpose : Open the global notification list, if it exists. Parameters: lppNotificationList -> returned notification list lphmemNotificationList -> returned shared memory handle lphmtxNotificationList -> returned Mutex handle lphevNotificationList -> returned event handle Returns : HRESULT Comment : This function does not effect the globals! ***************************************************************************/ HRESULT OpenNotifySession(LPNOTIFICATION_LIST * lppNotificationList, LPHANDLE lphmemNotificationList, LPHANDLE lphmtxNotificationList, LPHANDLE lphevNotificationList) { HRESULT hResult = hrSuccess; BOOL fMutex = FALSE; TCHAR szName[CharSizeOf(szNotificationName) + CharSizeOf(szMEM)]; StrCpyN(szName, szNotificationName, ARRAYSIZE(szName)); StrCatBuff(szName, szMTX, ARRAYSIZE(szName)); if (! (*lphmtxNotificationList = OpenMutex(SYNCHRONIZE, FALSE, // inherit handle? szName))) { DebugTrace("OpenNotifySession:OpenMutex(%s) -> %u\n", szName, GetLastError()); // No Advise sessions exist, don't bother with this. hResult = ResultFromScode(WAB_W_NO_ADVISE); goto exit; } if (hResult = HrWaitForObject(*lphmtxNotificationList, NOTIFY_CREATE_TIMEOUT)) { DebugTrace("CreateNotifySession:Mutex wait failed\n"); goto exit; } fMutex = TRUE; StrCpyN(szName, szNotificationName, ARRAYSIZE(szName)); StrCatBuff(szName, szMEM, ARRAYSIZE(szName)); if ((*lphmemNotificationList = CreateFileMapping(INVALID_HANDLE_VALUE, // handle NULL, // security descriptor PAGE_READWRITE | SEC_RESERVE, // reserve more 0, // max size high MAX_NOTIFICATION_SPACE, // max size low szName)) == NULL) { // name DebugTrace("CreateNotifySession: CreateFileMapping --> %u\n", GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } if ((*lppNotificationList = (LPNOTIFICATION_LIST)MapViewOfFile(*lphmemNotificationList, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, sizeof(NOTIFICATION_LIST))) == NULL) { DebugTrace("CreateNotifySession: CreateFileMapping --> %u\n", GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } // Initialize global notification list Assert((*lppNotificationList)->cAdvises != 0); // Number of advise processes // Notification Event StrCpyN(szName, szNotificationName, ARRAYSIZE(szName)); StrCatBuff(szName, szEVT, ARRAYSIZE(szName)); if (! (*lphevNotificationList = CreateEvent(NULL, TRUE, // Manual reset FALSE, // initial state (not triggered) szName))) { DebugTrace("OpenNotifySession:CreateEvent(%S) -> %u\n", szName, GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } exit: if (fMutex) { ReleaseMutex(*lphmtxNotificationList); } if (hResult) { // Failure, clean up if (*lphmemNotificationList) { CloseHandle(*lphmemNotificationList); *lphmemNotificationList = NULL; } if (*lphmtxNotificationList) { CloseHandle(*lphmtxNotificationList); *lphmtxNotificationList = NULL; } if (*lphevNotificationList) { CloseHandle(*lphevNotificationList); *lphevNotificationList = NULL; } } return(hResult); } #endif // NEW_STUFF /*************************************************************************** Name : HrWaitForObject Purpose : Wait for an object to be signalled Parameters: handle = object handle dwTimeout = timeout in milliseconds Returns : HRESULT Comment : ***************************************************************************/ HRESULT HrWaitForObject(HANDLE handle, DWORD dwTimeout) { switch (WaitForSingleObject(handle, dwTimeout)) { case WAIT_ABANDONED: DebugTrace(TEXT("WARNING:HrWaitForObject got WAIT_ABANDONED\n")); // fall through to success case WAIT_OBJECT_0: return(hrSuccess); case WAIT_TIMEOUT: DebugTrace(TEXT("HrWaitForObject timed out\n")); return(ResultFromScode(MAPI_E_TIMEOUT)); case WAIT_FAILED: default: DebugTrace(TEXT("HrWaitForObject failed -> %u\n"), GetLastError()); return(ResultFromScode(MAPI_E_CALL_FAILED)); } } /*************************************************************************** Name : HrWABNotify Purpose : Scans registered clients and Notifies them of a store modification The first-cut at notifications is extremely simplistic. Any time the WAB store changes, we fire off a store notification. No attempt to check eventmasks or entryids etc Parameters: lpIAB = THIS object Returns : HRESULT Comment : What happens in here: ***************************************************************************/ HRESULT HrWABNotify(LPIAB lpIAB) { HRESULT hResult = hrSuccess; LPADVISE_NODE lpAdviseNode = NULL; NOTIFICATION WABNotif = {0}; EnterCriticalSection(&lpIAB->cs); if (!lpIAB->pWABAdviseList || !lpIAB->pWABAdviseList->cAdvises) { hResult = ResultFromScode(MAPI_E_NOT_FOUND); goto exit; } // Since calling applications may have no idea of container/folder changes, but may // call container based methods .. // update the list of WAB containers for that applicaiton so that GetContentsTable etc // will work correctly .. if(bAreWABAPIProfileAware(lpIAB)) HrGetWABProfiles(lpIAB); WABNotif.ulEventType = fnevObjectModified; WABNotif.info.obj.ulObjType = MAPI_ADDRBOOK; WABNotif.info.obj.cbEntryID = WABNotif.info.obj.cbParentID = WABNotif.info.obj.cbOldID = WABNotif.info.obj.cbOldParentID = 0; WABNotif.info.obj.lpEntryID = WABNotif.info.obj.lpParentID = WABNotif.info.obj.lpOldID = WABNotif.info.obj.lpOldParentID = NULL; WABNotif.info.obj.lpPropTagArray = NULL; lpAdviseNode = lpIAB->pWABAdviseList->lpNode; while(lpAdviseNode) { lpAdviseNode->lpAdviseSink->lpVtbl->OnNotify(lpAdviseNode->lpAdviseSink, 1, &WABNotif); lpAdviseNode = lpAdviseNode->lpNext; } exit: LeaveCriticalSection(&lpIAB->cs); return(hResult); } /*************************************************************************** Name : HrAdvise Purpose : Performs client notification registration Parameters: lpIAB = THIS object cbEntryID = sizeof lpEntryID lpEntryID -> EntryID of object about which notifications should be generated. ulEventMask = events about which to generate notifications fnevObjectCreated fnevObjectDeleted fnevObjectModified fnevTableModified NOTE: WAB currently does not support fnevCriticalError, fnevObjectCopied or fnevObjectMoved. lpAdviseSink -> Client's advise sink object lpulConnection -> returned connection number (client should save to pass to Unadvise.) Returns : HRESULT Comment : What happens in here: Store the EventMask and AdviseSink in the local advise list. If there are no other Advise sessions open in this process: Make sure there is one and register it ***************************************************************************/ HRESULT HrAdvise(LPIAB lpIAB, ULONG cbEntryID, LPENTRYID lpEntryID, ULONG ulEventMask, LPMAPIADVISESINK lpAdvise, ULONG FAR * lpulConnection) { HRESULT hResult = hrSuccess; BOOL fExisted = FALSE; LPADVISE_NODE lpAdviseNode = NULL, lpTemp = NULL; static ULONG ulNextConnection = 1; EnterCriticalSection(&lpIAB->cs); if(!lpIAB->pWABAdviseList) { lpIAB->pWABAdviseList = LocalAlloc(LMEM_ZEROINIT, sizeof(ADVISE_LIST)); if(!lpIAB->pWABAdviseList) { hResult = MAPI_E_NOT_ENOUGH_MEMORY; goto exit; } lpIAB->pWABAdviseList->cAdvises = 0; lpIAB->pWABAdviseList->lpNode = NULL; } lpAdviseNode = LocalAlloc(LMEM_ZEROINIT, sizeof(ADVISE_NODE) + cbEntryID); if(!lpAdviseNode) { hResult = MAPI_E_NOT_ENOUGH_MEMORY; goto exit; } lpAdviseNode->ulConnection = ulNextConnection++; lpAdviseNode->ulEventMask = ulEventMask; lpAdviseNode->lpAdviseSink = lpAdvise; if(cbEntryID && lpEntryID) { CopyMemory(&lpAdviseNode->EntryID, lpEntryID, cbEntryID); } lpAdviseNode->lpPrev = NULL; lpAdviseNode->lpNext = lpIAB->pWABAdviseList->lpNode; if(lpIAB->pWABAdviseList->lpNode) lpIAB->pWABAdviseList->lpNode->lpPrev = lpAdviseNode; lpIAB->pWABAdviseList->lpNode = lpAdviseNode; lpIAB->pWABAdviseList->cAdvises++; // Addref the LPADVISESINK pointer so we have a handle on it ... // lpAdvise->lpVtbl->AddRef(lpAdvise); *lpulConnection = lpAdviseNode->ulConnection; exit: LeaveCriticalSection(&lpIAB->cs); return(hResult); #ifdef NEW_STUFF /* // Walk the advise list looking for the connection // Make sure we're safe to monkey with the list if (hResult = HrWaitForObject(hmtxAdviseList, ADVISE_TIMEOUT)) { DebugTrace("HrUnadvise:Mutex wait failed\n"); goto exit; } fMutex = TRUE; // Is there an open Advise session for this process? // If not, set up the advise session for this process. if (! lpNotificationList) { if (hResult = CreateNotifySession(&fExisted)) { DebugTraceResult( TEXT("HrAdvise:CreateNotifySession"), hResult); goto exit; } } // Add Advise info to Local Advise List. // Create the new node if (! (lpAdviseNode = LocalAlloc(LPTR, sizeof(ADVISE_NODE) + cbEntryID))) { DebugTrace("LocalAlloc(%u) AdviseNode -> %u\n", sizeof(ADVISE_NODE) + cbEntryID, GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } lpAdviseNode->ulConnection = ulNextConnection++; lpAdviseNode->lpAdviseSink = lpAdvise; lpAdviseNode->ulEventMask = ulEventMask; lpAdviseNode->cbEntryID = cbEntryID; CopyMemory(&lpAdviseNode->EntryID, lpEntryID, cbEntryID); // Add the new node to front of the list // Make sure we're safe to monkey with the list if (hResult = HrWaitForObject(hmtxAdviseList, ADVISE_TIMEOUT)) { DebugTrace("HrAdvise:Mutex wait failed\n"); goto exit; } fMutex = TRUE; lpAdviseNode->lpNext = AdviseList.lpNode; AdviseList.lpNode = lpAdviseNode; AdviseList.cAdvises++; *lpulConnection = lpAdviseNode->ulConnection; exit: if (fMutex) { ReleaseMutex(hmtxAdviseList); } #else hResult = ResultFromScode(MAPI_E_CALL_FAILED); */ #endif } /*************************************************************************** Name : HrUnadvise Purpose : Removes an Advise from the list Parameters: ulConnection = connection number to remove Returns : HRESULT Comment : ***************************************************************************/ HRESULT HrUnadvise(LPIAB lpIAB, ULONG ulConnection) { HRESULT hResult = hrSuccess; BOOL fMutex = FALSE; LPADVISE_NODE lpAdviseNode = NULL; EnterCriticalSection(&lpIAB->cs); if (!lpIAB->pWABAdviseList || !lpIAB->pWABAdviseList->cAdvises) { hResult = ResultFromScode(MAPI_E_NOT_FOUND); goto exit; } lpAdviseNode = lpIAB->pWABAdviseList->lpNode; while (lpAdviseNode) { if (lpAdviseNode->ulConnection == ulConnection) { if(lpIAB->pWABAdviseList->lpNode == lpAdviseNode) lpIAB->pWABAdviseList->lpNode = lpAdviseNode->lpNext; if(lpAdviseNode->lpPrev) lpAdviseNode->lpPrev->lpNext = lpAdviseNode->lpNext; if(lpAdviseNode->lpNext) lpAdviseNode->lpNext->lpPrev = lpAdviseNode->lpPrev; // Release the hold on this pointer ... lpAdviseNode->lpAdviseSink->lpVtbl->Release(lpAdviseNode->lpAdviseSink); LocalFreeAndNull(&lpAdviseNode); lpIAB->pWABAdviseList->cAdvises--; //Assert(lpIAB->pWABAdviseList->cAdvises == 0 && lpIAB->pWABAdviseList->lpNode == NULL); if(!lpIAB->pWABAdviseList->cAdvises && !lpIAB->pWABAdviseList->lpNode) { LocalFree(lpIAB->pWABAdviseList); lpIAB->pWABAdviseList = NULL; } goto exit; } lpAdviseNode = lpAdviseNode->lpNext; } hResult = ResultFromScode(MAPI_E_NOT_FOUND); exit: LeaveCriticalSection(&lpIAB->cs); return(hResult); /* #ifdef NEW_STUFF BOOL fMutex = FALSE; LPADVISE_NODE lpAdviseNode = NULL; LPADVISE_NODE * lppPrevNode = &(AdviseList.lpNode); if (hmtxAdviseList == NULL || AdviseList.cAdvises == 0) { hResult = ResultFromScode(MAPI_E_NOT_FOUND); goto exit; } // Walk the advise list looking for the connection // Make sure we're safe to monkey with the list if (hResult = HrWaitForObject(hmtxAdviseList, ADVISE_TIMEOUT)) { DebugTrace("HrUnadvise:Mutex wait failed\n"); goto exit; } fMutex = TRUE; lpAdviseNode = AdviseList.lpNode; while (lpAdviseNode) { if (lpAdviseNode->ulConnection == ulConnection) { // Found it, remove from list *lppPrevNode = lpAdviseNode->lpNext; // BUGBUG: Don't forget to remove any notifications that haven't been // processed by this process yet. // Free the node LocalFreeAndNull(&lpAdviseNode); goto exit; } lppPrevNode = &(lpAdviseNode->lpNext); lpAdviseNode = lpAdviseNode->lpNext; } hResult = ResultFromScode(MAPI_E_NOT_FOUND); exit: if (fMutex) { ReleaseMutex(hmtxAdviseList); } #else hResult = ResultFromScode(MAPI_E_CALL_FAILED); #endif return(hResult); */ } /*************************************************************************** Name : HrFireNotification Purpose : Fire a notification Parameters: lpNotification -> NOTIFICATION structure Returns : HRESULT Comment : What happens in here: if shared memory exists Map in the shared memory Add the notification to the global Advise list Set the count on this notification to the global advise count. trigger the Global Advise Event. ***************************************************************************/ HRESULT HrFireNotification(LPNOTIFICATION lpNotification) { HRESULT hResult = hrSuccess; #ifdef NEW_STUFF LPNOTIFICATION_LIST lpNotifyList = NULL; HANDLE hmemNotifyList = NULL; HANDLE hmtxNotifyList = NULL; HANDLE hevNotifyList = NULL; LPNOTIFICATION_NODE lpNewNode = NULL, lpTempNode, *lppPrevNode; BOOL fNotifyMutex = FALSE, fAdviseMutex = FALSE; BOOL fOpened = FALSE; Assert(lpNotification); // If there is an Advise session, use it, else create a temporary // Notification session if (lpNotificationList) { lpNotifyList = lpNotificationList; hmtxNotifyList = hmtxNotificationList; hevNotifyList = hevNotificationList; } else { if (hResult = OpenNotifySession(&lpNotifyList, &hmemNotifyList, &hmtxNotifyList, &hevNotifyList)) { DebugTraceResult( TEXT("HrAdvise:OpenNotifySession"), hResult); // No waiting advise sessions, there's no point in continuing goto exit; } fOpened = TRUE; } // Request access to the Global Notification List if (hResult = HrWaitForObject(hmtxNotifyList, FIRE_NOTIFY_TIMEOUT)) { DebugTrace("HrFireNotification:Mutex wait failed\n"); goto exit; } fNotifyMutex = TRUE; // Add the notification to the beginning of the global notification list // create a new node for it if (! (lpNewNode = LocalAlloc(LPTR, sizeof(NOTIFICATION_NODE)))) { DebugTrace("LocalAlloc(%u) NotificationNode -> %u\n", sizeof(NOTIFICATION_NODE), GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } lpNewNode->cbSize = sizeof(NOTIFICATION_NODE); // BUGBUG: This doesn't copy the stuff pointed to in the notification structure! CopyMemory(&lpNewNode->Notification, lpNotification, sizeof(NOTIFICATION)); // Add the new node to END of the list. Note that it must go to the end // of the list so that the unique identifiers are kept in order. // Make sure we're safe to monkey with the Advise list if (hResult = HrWaitForObject(hmtxAdviseList, ADVISE_TIMEOUT)) { DebugTrace("HrAdvise:Mutex wait failed\n"); goto exit; } fAdviseMutex = TRUE; lpNewNode->lpNext = NULL; lpTempNode = lpNotifyList->lpNode; lppPrevNode = &lpNotifyList->lpNode; while (lpTempNode) { lppPrevNode = &lpTempNode->lpNext; lpTempNode = lpTempNode->lpNext; } *lppPrevNode = lpNewNode; // Set the count on this notification to the global // advise count. lpNewNode->ulCount = lpNotificationList->cAdvises; // Set the unique identifier for this notification lpNewNode->ulIdentifier = lpNotifyList->ulNextIdentifier++; // trigger the Global Advise Event. if (! PulseEvent(hevNotifyList)) { DebugTrace("HrFireNotification:PulseEvent -> %u\n", GetLastError()); // what are ya gonna do? hResult = ResultFromScode(MAPI_E_CALL_FAILED); goto exit; } exit: if (fNotifyMutex) { ReleaseMutex(hmtxNotifyList); } if (fAdviseMutex) { ReleaseMutex(hmtxAdviseList); } // Clean up the stuff if we opened it. if (fOpened) { if (lpNotifyList) { UnmapViewOfFile(lpNotifyList); } if (hmemNotifyList) { CloseHandle(hmemNotifyList); } if (hmtxNotifyList) { CloseHandle(hmtxNotifyList); } if (hevNotifyList) { CloseHandle(hevNotifyList); } } #else hResult = ResultFromScode(MAPI_E_CALL_FAILED); #endif return(hResult); } #ifdef NEW_STUFF /*************************************************************************** Name : AdviseThread Purpose : Thread routine for advise Parameters: lpdwParam = Thread parameter Returns : DWORD return code. Comment : What happens in here: loop until Unadvise wait for trigger of the Global Advise Event or Unadvise event if Advise Event Loop through global advise list if we haven't already dealt with this notification check events in global advise list against local advise list if match call client's NotifCallback Decrement count in this notification if count == 0 remove this item from the global advise list if Unadvise decrement global advise count exit thread ***************************************************************************/ DWORD AdviseThread(LPDWORD lpdwParam) { BOOL fNotifyMutex = FALSE, fAdviseMutex = FALSE; LPNOTIFICATION_NODE lpNotifyNode = NULL, *lppNotifyPrev; LPADVISE_NODE lpAdviseNode = NULL; // loop until Unadvise while (TRUE) { // wait for trigger of the Global Advise Event or Unadvise event switch(WaitForTwoObjects(hevNotificationList, hevKillAdvise, ADVISE_THREAD_TIMEOUT)) { case 0: // New notification break; case (ULONG)-1: // error DebugTrace("AdviseThread:WaitForTwoObjects error\n"); // fall through to kill case 1: // kill advise DebugTrace("Terminating AdviseThread\n"); goto exit; } // New notification // Loop through global notification list // Gain access to list // wait for trigger of the Global Advise Event or Unadvise event switch(WaitForTwoObjects(hmtxNotificationList, hevKillAdvise, NOTIFY_ADVISE_TIMEOUT)) { case 0: // Got the List Mutex fNotifyMutex = TRUE; break; case (ULONG)-1: // error DebugTrace("AdviseThread:WaitForTwoObjects error\n"); // fall through to kill case 1: // kill advise DebugTrace("Terminating AdviseThread\n"); goto exit; } Assert(fNotifyMutex); // Also need to look at the local advise list switch(WaitForTwoObjects(hmtxAdviseList, hevKillAdvise, NOTIFY_ADVISE_TIMEOUT)) { case 0: // Got the List Mutex fAdviseMutex = TRUE; break; case (ULONG)-1: // error DebugTrace("AdviseThread:WaitForTwoObjects error\n"); // fall through to kill case 1: // kill advise DebugTrace("Terminating AdviseThread\n"); goto exit; } Assert(fAdviseMutex); lpNotifyNode = lpNotificationList->lpNode; lppNotifyPrev = &(lpNotificationList->lpNode); while (lpNotifyNode) { // if we haven't already dealt with this notification if (lpNotifyNode->ulIdentifier > ulMaxIdentifierSeen) { // We haven't seen this one yet. Process it. // NOTE: For this to work, new notification nodes must be added // at the END of the notification list! ulMaxIdentifierSeen = lpNotifyNode->ulIdentifier; // check this notification event against local advise list lpAdviseNode = AdviseList.lpNode; while (lpAdviseNode) { if (lpNotifyNode->Notification.ulEventType & lpAdviseNode->ulEventMask) { // Right event type, is it the right object? switch (lpNotifyNode->Notification.ulEventType) { case fnevCriticalError: // ERROR_NOTIFICATION if (CompareEntryIDs(lpAdviseNode->cbEntryID, (LPENTRYID)&lpAdviseNode->EntryID, lpNotifyNode->Notification.info.err.cbEntryID, lpNotifyNode->Notification.info.err.lpEntryID)) { // This is it! // Call the notification callback lpAdviseNode->lpAdviseSink->lpVtbl->OnNotify(lpAdviseNode->lpAdviseSink, 1, &lpNotifyNode->Notification); } break; case fnevObjectCreated: case fnevObjectDeleted: case fnevObjectModified: case fnevObjectCopied: case fnevObjectMoved: case fnevSearchComplete: // OBJECT_NOTIFICATION if (CompareEntryIDs(lpAdviseNode->cbEntryID, (LPENTRYID)&lpAdviseNode->EntryID, lpNotifyNode->Notification.info.obj.cbEntryID, lpNotifyNode->Notification.info.obj.lpEntryID)) { // This is it! // Call the notification callback lpAdviseNode->lpAdviseSink->lpVtbl->OnNotify(lpAdviseNode->lpAdviseSink, 1, &lpNotifyNode->Notification); } break; case fnevTableModified: // TABLE_NOTIFICATION // BUGBUG: NYI break; default: break; } } lpAdviseNode = lpAdviseNode->lpNext; } // Decrement count in this notification // if count == 0 // remove this item from the global notification list if (--lpNotifyNode->ulCount == 0) { *lppNotifyPrev = lpNotifyNode->lpNext; LocalFree(lpNotifyNode); lpNotifyNode = *lppNotifyPrev; } else { lpNotifyNode = lpNotifyNode->lpNext; } } } if (fNotifyMutex) { fNotifyMutex = FALSE; ReleaseMutex(hmtxNotificationList); } if (fAdviseMutex) { fAdviseMutex = FALSE; ReleaseMutex(hmtxAdviseList); } } exit: // exit thread return(0); } #endif