|
|
/*
* NTFY.C * * MAPI cross-process notification engine. */
#include "_apipch.h"
#ifdef OLD_STUFF
#include "_mapipch.h"
#include <stddef.h>
#ifdef MAC
#include <utilmac.h>
#define PvGetInstanceGlobals() PvGetInstanceGlobalsMac(kInstMAPIX)
#define PvGetInstanceGlobalsEx(_x) PvGetInstanceGlobalsMac(kInstMAPIX)
#endif
#pragma SEGMENT(Notify)
#endif // OLD_STUFF
/*
* Un-comment this line to exercise HrThisThreadAdviseSink * thoroughly. (It will be used to wrap all advise sinks except * those registered for synchronous notifications.) */ //#define WRAPTEST 1
/* Event and flags validation for subscribe and notify */ #define fnevReserved 0x3FFFFC00
#define fnevReservedInternal 0x7FFFFC00
#define ulSubscribeReservedFlags 0xBFFFFFFF
#define ulNotifyReservedFlags 0xFFFFFFFF
/* Additional stuff for SREG.ulFlagsAndRefcount */ #define SREG_DELETING 0x10000
#define AddRefCallback(_psreg) ++((_psreg)->ulFlagsAndRefcount)
#define ReleaseCallback(_psreg) --((_psreg)->ulFlagsAndRefcount)
#define IsRefCallback(_psreg) ((((_psreg)->ulFlagsAndRefcount) & 0xffff) != 0)
#ifdef WIN16
#define GetClassInfoA GetClassInfo
#define WNDCLASSA WNDCLASS
#define StrCpyNA StrCpyN
#endif
/* special spooler handling */ #define hwndNoSpooler ((HWND) 0)
#define FIsKeyOlaf(pkey) \
(pkey->cb == ((LPNOTIFKEY) ¬ifkeyOlaf)->cb && \ memcmp(pkey->ab, ((LPNOTIFKEY) ¬ifkeyOlaf)->ab, \ (UINT) ((LPNOTIFKEY) ¬ifkeyOlaf)->cb) == 0) extern CHAR szSpoolerCmd[]; #ifdef OLD_STUFF
CHAR szSpoolerCmd[] = "MAPISP" szMAPIDLLSuffix ".EXE"; #else
CHAR szSpoolerCmd[] = "MAPISP32.EXE"; #endif // OLD_STUFF
SizedNOTIFKEY (5,notifkeyOlaf) = {5, "Olaf"}; // no TEXT!
/*
* Notification window message. * * It is sent to a specific window handle, not broadcast. We could * use the WM_USER range, but instead we use a registered window * message; this will make it easier for special MAPI apps (such as * test scripting) to handle notify messages. * * The WPARAM is set to 1 if the notification is synchronous, and 0 * if it is asynchronous. * * The LPARAM is unused for asynchronous notifications. * For synchronous notifications, the LPARAM is the offset of the * notification parameters in the shared memory area. */
/*
* Name and message number for our notification window message. */
UINT wmNotify = 0; #pragma SHARED_BEGIN
CHAR szNotifyMsgName[] = szMAPINotificationMsg;
/*
* SKEY * * Stores a notification key and associated information in shared * memory. * * The key has a reference count and a list of registrations * (SREG structures) attached to it. */
typedef struct { int cRef; GHID ghidRegs; // first registration in chain (SREG)
NOTIFKEY key; // copy of key from Subscribe()
} SKEY, FAR *LPSKEY;
/*
* SREG * * Shared information about a registration. Lives in a list hung * off the key for which it was registered. */ typedef struct { GHID ghidRegNext; // next registration in chain (SREG)
GHID ghidKey; // the key that owns this registration
HWND hwnd; // process's notification window handle
// parameters copied from Subscribe...
ULONG ulEventMask; LPMAPIADVISESINK lpAdvise; ULONG ulConnection; ULONG ulFlagsAndRefcount; // ulFlags parameter + callback refcount
} SREG, FAR *LPSREG;
/*
* SPARMS * * Stores notification parameters from Notify() in shared memory. * * Includes a reference to the key being notified, so the callback * addresses can be looked up in the target process. * * Includes the original shared memory offset, so that pointers * within the notification parameters can be relocated in the * target process (yecch!). * * The offset of this structure in shared memory is passed as the * LPARAM of the notification window message. */
#pragma warning(disable:4200) // zero length byte array
typedef struct { int cRef; // # of unprocessed messages
GHID ghidKey; // smem offset of parent key
ULONG cNotifications; // # of NOTIFICATION structures in ab
LPVOID pvRef; // original shared memory offset
ULONG cb; // size of ab
#if defined (_AMD64_) || defined(_IA64_)
ULONG ulPadThisSillyThingForRisc; #endif
BYTE ab[]; // Actual notification parameters
} SPARMS, FAR *LPSPARMS; #pragma warning(default:4200) // zero length byte array
/*
* Temporary placeholder for notification. Remembers the window * handle, task queue, and whether it's synchronous or not. */ typedef struct { HWND hwnd; int fSync; GHID ghidTask; } TREG, FAR *LPTREG;
// Notification window cruft.
char szNotifClassName[] = "WMS notif engine"; char szNotifWinName[] = "WMS notif window";
#pragma SHARED_END
#ifdef DEBUG
BOOL fAlwaysValidateKeys = FALSE; #endif
// Local functions
//$MAC - Bad Prototype
#ifndef MAC
LRESULT STDAPICALLTYPE LNotifWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); #else
LRESULT CALLBACK LNotifWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); #endif
LRESULT STDAPICALLTYPE DrainNotifQueue(BOOL fSync, ULONG ibParms); BOOL FBadIUnknownComplete(LPVOID lpv); SCODE ScFindKey(LPNOTIFKEY pkey, HGH hgh, LPSHDR pshdr, ULONG FAR *pikey); void Unregister(LPINST pinst, GHID ghidKey, GHID ghidReg, LPMAPIADVISESINK FAR *ppadvise); void ReleaseKey(LPINST pinst, ULONG ibKey); SCODE ScFindTask(LPINST pinst, HWND hwndNotify, HGH hgh, PGHID pghidTask, PGHID pghidTaskPrev); void CleanupTask(LPINST pinst, HWND hwndNotify, BOOL fGripe); BOOL IsValidTask( HWND hwnd, LPINST pinst ); SCODE ScEnqueueParms(LPINST pinst, HGH hgh, GHID ghidTask, GHID ghidParms); SCODE ScDequeueParms(HGH hgh, LPSTASK pstask, LPNOTIFKEY lpskeyFilter, PGHID pghidParms); BOOL FValidReg(HGH hgh, LPSHDR pshdr, GHID ghidReg); BOOL FValidKey(HGH hgh, LPSHDR pshdr, GHID ghidKey); BOOL FValidParms(HGH hgh, LPSHDR pshdr, GHID ghidParms); BOOL FValidRgkey(HGH hgh, LPSHDR pshdr); BOOL FSortedRgkey(HGH hgh, LPSHDR pshdr); #ifdef WIN16
void CheckTimezone(SYSTEMTIME FAR *pst); // in DT.C
SCODE ScGetInstRetry(LPINST FAR *ppinst); #else
#define ScGetInstRetry(ppi) ScGetInst(ppi)
#endif
SCODE ScNewStask(HWND hwnd, LPSTR szTask, ULONG ulFlags, HGH hgh, LPSHDR pshdr); SCODE ScNewStubReg(LPINST pinst, LPSHDR pshdr, HGH hgh); VOID DeleteStubReg(LPINST pinst, LPSHDR pshdr, HGH hgh); SCODE ScSubscribe(LPINST pinst, HGH hgh, LPSHDR pshdr, LPADVISELIST FAR *lppList, LPNOTIFKEY lpKey, ULONG ulEventMask, LPMAPIADVISESINK lpAdvise, ULONG ulFlags, ULONG FAR *lpulConnection);
#ifdef OLD_STUFF
/* MAPI support object methods */
STDMETHODIMP MAPISUP_Subscribe( LPSUPPORT lpsupport, LPNOTIFKEY lpKey, ULONG ulEventMask, ULONG ulFlags, LPMAPIADVISESINK lpAdvise, ULONG FAR *lpulConnection) { HRESULT hr;
#ifdef PARAMETER_VALIDATION
if (IsBadWritePtr(lpsupport, sizeof(SUPPORT))) { DebugTraceArg(MAPISUP_Subscribe, "lpsupport fails address check"); goto badArg; }
if (IsBadReadPtr(lpsupport->lpVtbl, sizeof(MAPISUP_Vtbl))) { DebugTraceArg(MAPISUP_Subscribe, "lpsupport->lpVtbl fails address check"); goto badArg; }
if (ulEventMask & fnevReservedInternal) { DebugTraceArg(MAPISUP_Subscribe, "reserved event flag used"); goto badArg; }
// Remainder of parameters checked in HrSubscribe
#endif /* PARAMETER_VALIDATION */
// The notification object listhead lives in the support object
hr = HrSubscribe(&lpsupport->lpAdviseList, lpKey, ulEventMask, lpAdvise, ulFlags, lpulConnection);
if (hr != hrSuccess) { UINT ids; SCODE sc = GetScode(hr); ULONG ulContext = CONT_SUPP_SUBSCRIBE_1;
if (sc == MAPI_E_NOT_ENOUGH_MEMORY) ids = IDS_NOT_ENOUGH_MEMORY; else if (sc == MAPI_E_NOT_INITIALIZED) ids = IDS_MAPI_NOT_INITIALIZED; else ids = IDS_CALL_FAILED;
SetMAPIError(lpsupport, hr, ids, NULL, ulContext, 0, 0, NULL); }
DebugTraceResult(MAPISUP_Subscribe, hr); return hr;
#ifdef PARAMETER_VALIDATION
badArg: #endif
return ResultFromScode(MAPI_E_INVALID_PARAMETER); }
STDMETHODIMP MAPISUP_Unsubscribe(LPSUPPORT lpsupport, ULONG ulConnection) { HRESULT hr;
#ifdef PARAMETER_VALIDATION
if (IsBadWritePtr(lpsupport, sizeof(SUPPORT))) { DebugTraceArg(MAPISUP_Subscribe, "lpsupport fails address check"); goto badArg; }
if (IsBadReadPtr(lpsupport->lpVtbl, sizeof(MAPISUP_Vtbl))) { DebugTraceArg(MAPISUP_Subscribe, "lpsupport->lpVtbl fails address check"); goto badArg; } #endif
hr = HrUnsubscribe(&lpsupport->lpAdviseList, ulConnection);
if (hr != hrSuccess) { UINT ids; SCODE sc = GetScode(hr); ULONG ulContext = CONT_SUPP_UNSUBSCRIBE_1;
if (sc == MAPI_E_NOT_ENOUGH_MEMORY) ids = IDS_NOT_ENOUGH_MEMORY; else if (sc == MAPI_E_NOT_FOUND) ids = IDS_NO_CONNECTION; else if (sc == MAPI_E_NOT_INITIALIZED) ids = IDS_MAPI_NOT_INITIALIZED; else ids = IDS_CALL_FAILED;
SetMAPIError(lpsupport, hr, ids, NULL, ulContext, 0, 0, NULL); }
DebugTraceResult(MAPISUP_Unsubscribe, hr); return hr;
#ifdef PARAMETER_VALIDATION
badArg: #endif
return ResultFromScode(E_INVALIDARG); }
STDMETHODIMP MAPISUP_Notify( LPSUPPORT lpsupport, LPNOTIFKEY lpKey, ULONG cNotification, LPNOTIFICATION lpNotification, ULONG * lpulFlags) { HRESULT hr;
#ifdef PARAMETER_VALIDATION
if (IsBadWritePtr(lpsupport, sizeof(SUPPORT))) { DebugTraceArg(MAPISUP_Notify, "lpsupport fails address check"); goto badArg; }
if (IsBadReadPtr(lpsupport->lpVtbl, sizeof(MAPISUP_Vtbl))) { DebugTraceArg(MAPISUP_Notify, "lpsupport->lpVtbl fails address check"); goto badArg; }
// Remainder of parameters checked in HrNotify
#endif /* PARAMETER_VALIDATION */
// The notification object listhead lives in the support object
hr = HrNotify(lpKey, cNotification, lpNotification, lpulFlags);
if (hr != hrSuccess) { UINT ids; SCODE sc = GetScode(hr); ULONG ulContext = CONT_SUPP_NOTIFY_1;
if (sc == MAPI_E_NOT_ENOUGH_MEMORY) ids = IDS_NOT_ENOUGH_MEMORY; else if (sc == MAPI_E_NOT_INITIALIZED) ids = IDS_MAPI_NOT_INITIALIZED; else ids = IDS_CALL_FAILED;
SetMAPIError(lpsupport, hr, ids, NULL, ulContext, 0, 0, NULL); }
DebugTraceResult(MAPISUP_Notify, hr); return hr;
#ifdef PARAMETER_VALIDATION
badArg: #endif
return ResultFromScode(MAPI_E_INVALID_PARAMETER); }
/* End of support object methods */ #endif // OLD_STUFF
/* Notification engine exported functions */
/*
* ScInitNotify * * Initialize the cross-process notification engine. * * Note: reference counting is handled by the top-level routine * ScInitMapiX; it is not necessary here. */ SCODE ScInitNotify( LPINST pinst ) { SCODE sc = S_OK; HGH hgh = NULL; GHID ghidstask = 0; LPSTASK pstask = NULL; LPSHDR pshdr; HINSTANCE hinst = HinstMapi(); WNDCLASSA wc; HWND hwnd = NULL;
#ifdef DEBUG
fAlwaysValidateKeys = GetPrivateProfileInt("MAPIX", "CheckNotifKeysOften", 0, "mapidbg.ini"); #endif
// Register the window class. Ignore any failures; handle those
// when the window is created.
if (!GetClassInfoA(hinst, szNotifClassName, &wc)) { ZeroMemory(&wc, sizeof(WNDCLASSA)); wc.style = CS_GLOBALCLASS; wc.hInstance = hinst; wc.lpfnWndProc = LNotifWndProc; wc.lpszClassName = szNotifClassName;
(void)RegisterClassA(&wc); }
// Create the window.
hwnd = CreateWindowA(szNotifClassName, szNotifWinName, WS_POPUP, // bug 6111: pass on Win95 hotkey
0, 0, 0, 0, NULL, NULL, hinst, NULL); if (!hwnd) { DebugTrace("ScInitNotify: failure creating notification window (0x%lx)\n", GetLastError()); sc = MAPI_E_NOT_INITIALIZED; goto ret; }
// Register the window message
if (!(wmNotify = RegisterWindowMessageA(szNotifyMsgName))) { DebugTrace("ScInitNotify: failure registering notification window message\n"); sc = MAPI_E_NOT_INITIALIZED; goto ret; } pinst->hwndNotify = hwnd;
// The caller of this function should have already gotten
// the Global Heap Mutex.
hgh = pinst->hghShared; pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr);
// If we're the first one in and not the spooler, create the stub
// spooler information.
if (!(pinst->ulInitFlags & MAPI_SPOOLER_INIT) && !pshdr->ghidTaskList) { if (sc = ScNewStask(hwndNoSpooler, szSpoolerCmd, MAPI_SPOOLER_INIT, hgh, pshdr)) goto ret; if (sc = ScNewStubReg(pinst, pshdr, hgh)) goto ret; } // If we're the spooler and not the first one in, update the stub
// spooler information.
if ((pinst->ulInitFlags & MAPI_SPOOLER_INIT) && pshdr->ghidTaskList) { // Spin through and find the spooler.
for (ghidstask = pshdr->ghidTaskList; ghidstask; ) { pstask = (LPSTASK) GH_GetPv(hgh, ghidstask); if (pstask->uFlags & MAPI_TASK_SPOOLER) break; ghidstask = pstask->ghidTaskNext; pstask = NULL; } Assert(ghidstask && pstask); if (pstask) { DebugTrace("ScInitNotify: flipping stub spooler task\n"); pstask->hwndNotify = hwnd; } } else { // Initialize the shared memory information for this task.
if (sc = ScNewStask(hwnd, pinst->szModName, pinst->ulInitFlags, hgh, pshdr)) goto ret; }
ret: if (sc) { if (hwnd) { DestroyWindow(hwnd); pinst->hwndNotify = (HWND) 0; }
if (ghidstask) GH_Free(hgh, ghidstask); } DebugTraceSc(ScInitNotify, sc); return sc; }
/*
* DeinitNotify * * Shut down the cross-process notification engine. * * Note: reference counting is handled by the top-level routine * DeinitInstance; it is not necessary here. */ void DeinitNotify() { LPINST pinst; #ifdef WIN32
HINSTANCE hinst; WNDCLASSA wc; #endif
SCODE sc; HGH hgh; LPSHDR pshdr;
// SNEAKY: we're only called when it's safe to party on the INST,
// so we evade the lock.
pinst = (LPINST) PvGetInstanceGlobals(); if (!pinst || !pinst->hwndNotify) return; hgh = pinst->hghShared;
if (GH_WaitForMutex(hgh, INFINITE)) { pshdr = (LPSHDR) GH_GetPv(hgh, pinst->ghidshdr);
CleanupTask(pinst, pinst->hwndNotify, FALSE);
// If it's the spooler who is exiting, recreate the stub structures
if (pinst->ulInitFlags & MAPI_SPOOLER_INIT) { sc = ScNewStask(hwndNoSpooler, szSpoolerCmd, MAPI_SPOOLER_INIT, hgh, pshdr); DebugTraceSc(DeinitNotify: recreate stub task, sc); sc = ScNewStubReg(pinst, pshdr, hgh); DebugTraceSc(DeinitNotify: recreate stub reg, sc); }
GH_ReleaseMutex(hgh); } // else shared memory is toast
Assert(IsWindow(pinst->hwndNotify)); DestroyWindow(pinst->hwndNotify);
#ifdef WIN32
hinst = hinstMapiXWAB; if (GetClassInfoA(hinst, szNotifClassName, &wc)) UnregisterClassA(szNotifClassName, hinst); #endif
}
/*
* HrSubscribe * * Creates a notification object, and records all the parameters * for use when Notify() is later called. All the parameters are * stored in shared memory, where Notify() will later find them. * * lppHead Head of linked list of notification objects, * used for invalidation * lpKey Unique key for the object for which * callbacks are desired * ulEventMask Bitmask of events for which callback is * desired * lpAdvise the advise sink for callbacks * ulFlags Callback handling flags * lppNotify Address of the new object is placed * here * */ STDMETHODIMP HrSubscribe(LPADVISELIST FAR *lppList, LPNOTIFKEY lpKey, ULONG ulEventMask, LPMAPIADVISESINK lpAdvise, ULONG ulFlags, ULONG FAR *lpulConnection) { SCODE sc; LPINST pinst = NULL; HGH hgh = NULL; LPSHDR pshdr = NULL; #ifdef WRAPTEST
LPMAPIADVISESINK padviseOrig = NULL; #endif
#ifdef PARAMETER_VALIDATION
if (lppList) { if (IsBadWritePtr(lppList, sizeof(LPADVISELIST))) { DebugTraceArg(HrSubscribe, "lppList fails address check"); goto badArg; } if (*lppList && IsBadWritePtr(*lppList, offsetof(ADVISELIST, rgItems))) { DebugTraceArg(HrSubscribe, "*lppList fails address check"); goto badArg; } }
if (IsBadReadPtr(lpKey, (size_t)CbNewNOTIFKEY(0)) || IsBadReadPtr(lpKey, (size_t)CbNOTIFKEY(lpKey))) { DebugTraceArg(HrSubscribe, "lpKey fails address check"); goto badArg; }
if (ulEventMask & fnevReserved) { DebugTraceArg(HrSubscribe, "reserved event flag used"); goto badArg; }
if (FBadIUnknownComplete(lpAdvise)) { DebugTraceArg(HrSubscribe, "lpAdvise fails address check"); goto badArg; }
if (ulFlags & ulSubscribeReservedFlags) { DebugTraceArg(HrSubscribe, "reserved flags used"); return ResultFromScode(MAPI_E_UNKNOWN_FLAGS); }
if (IsBadWritePtr(lpulConnection, sizeof(ULONG))) { DebugTraceArg(HrSubscribe, "lpulConnection fails address check"); goto badArg; } #endif /* PARAMETER_VALIDATION */
#ifdef WRAPTEST
{ HRESULT hr;
if (!(ulFlags & NOTIFY_SYNC)) { if (lpAdvise) { padviseOrig = lpAdvise; if (HR_FAILED(hr = HrThisThreadAdviseSink(padviseOrig, &lpAdvise))) { DebugTraceResult(HrSubscribe: WRAPTEST failed, hr); return hr; } } else padviseOrig = NULL; } } #endif /* WRAPTEST */
if (sc = ScGetInst(&pinst)) goto ret; Assert(pinst->hwndNotify); Assert(IsWindow(pinst->hwndNotify));
hgh = pinst->hghShared;
// Lock shared memory
if (!GH_WaitForMutex(hgh, INFINITE)) { sc = MAPI_E_TIMEOUT; goto ret; }
pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr);
sc = ScSubscribe(pinst, hgh, pshdr, lppList, lpKey, ulEventMask, lpAdvise, ulFlags, lpulConnection); // fall through to ret;
ret: if (pshdr) GH_ReleaseMutex(hgh); ReleaseInst(&pinst);
if (!sc && lpAdvise) UlAddRef(lpAdvise);
#ifdef WRAPTEST
if (padviseOrig) { // Drop the ref we created for the purpose of wrapping
Assert(padviseOrig != lpAdvise); UlRelease(lpAdvise); } #endif
DebugTraceSc(HrSubscribe, sc); return ResultFromScode(sc);
#ifdef PARAMETER_VALIDATION
badArg: #endif
return ResultFromScode(MAPI_E_INVALID_PARAMETER); }
SCODE ScSubscribe(LPINST pinst, HGH hgh, LPSHDR pshdr, LPADVISELIST FAR *lppList, LPNOTIFKEY lpKey, ULONG ulEventMask, LPMAPIADVISESINK lpAdvise, ULONG ulFlags, ULONG FAR *lpulConnection) { SCODE sc; LPSKEY pskey = NULL; LPSREG psreg = NULL; PGHID pghidskey = NULL; ULONG ikey; int ckey; GHID ghidsreg = 0; BOOL fCleanupAdviseList = FALSE;
// Very, very special case: if this is spooler registering for
// client connection, get rid of the stub registration
if ((pinst->ulInitFlags & MAPI_SPOOLER_INIT) && FIsKeyOlaf(lpKey)) { DeleteStubReg(pinst, pshdr, hgh); }
// Copy other reg info to shared memory.
// Don't hook to key until everything that can fail is done.
if (!(ghidsreg = GH_Alloc(hgh, sizeof(SREG)))) goto oom;
psreg = (LPSREG)GH_GetPv(hgh, ghidsreg); psreg->hwnd = pinst->hwndNotify; psreg->ulEventMask = ulEventMask; psreg->lpAdvise = lpAdvise; psreg->ulFlagsAndRefcount = ulFlags;
// Hook advise sink to caller's list. The release key is the
// shared memory offset of the registration.
if (lppList && lpAdvise) { // AddRef happens in the subroutine for the AdviseList copy.
// The reference should be released by ScDelAdviseList()
//
if (FAILED(sc = ScAddAdviseList(NULL, lppList, lpAdvise, (ULONG)ghidsreg, 0, NULL))) goto ret;
fCleanupAdviseList = TRUE; }
// Copy key to shared memory, if it's not already there
#ifdef DEBUG
if (fAlwaysValidateKeys) Assert(FValidRgkey(hgh, pshdr)); #endif
sc = ScFindKey(lpKey, hgh, pshdr, &ikey);
if (sc == S_FALSE) { GHID ghidskey;
// Key was not found, we need to make a copy.
// Create key structure with null regs list
if (!(ghidskey = GH_Alloc(hgh, (UINT)(offsetof(SKEY, key.ab[0]) + lpKey->cb)))) goto oom;
pskey = (LPSKEY)GH_GetPv(hgh, ghidskey); MemCopy(&pskey->key, lpKey, offsetof(NOTIFKEY,ab[0]) + (UINT)lpKey->cb); pskey->ghidRegs = 0; pskey->cRef = 0;
// Add new key to sorted list of offsets
// First ensure there is room in the list
if (!pshdr->ghidKeyList) { // There is no list at all, create empty
Assert(pshdr->cKeyMac == 0); Assert(pshdr->cKeyMax == 0);
if (!(ghidskey = GH_Alloc(hgh, cKeyIncr*sizeof(GHID)))) goto oom;
pghidskey = (PGHID)GH_GetPv(hgh, ghidskey); ZeroMemory(pghidskey, cKeyIncr*sizeof(GHID)); pshdr->cKeyMax = cKeyIncr; pshdr->ghidKeyList = ghidskey; } else if (pshdr->cKeyMac >= pshdr->cKeyMax) { // List is full, grow it
Assert(pshdr->cKeyMax); Assert(pshdr->ghidKeyList);
if (!(ghidskey = GH_Realloc(hgh, pshdr->ghidKeyList, (pshdr->cKeyMax + cKeyIncr) * sizeof(GHID)))) { DebugTrace( "ScSubscribe: ghidskey can't grow.\n"); goto oom; }
pghidskey = (PGHID)GH_GetPv(hgh, ghidskey); pshdr->cKeyMax += cKeyIncr; pshdr->ghidKeyList = ghidskey; } else { // There's room
pghidskey = (PGHID)GH_GetPv(hgh, pshdr->ghidKeyList); } //
// BEYOND THIS POINT, NOTHING IS ALLOWED TO FAIL.
// The error recovery code assumes this; specifically, it only
// undoes allocations, not modifications to data structures.
//
// Shift any elements after the insertion point up by one,
// and insert the new key. We compute the ghid because we've
// been reusing the ghidskey thing for other allocations.
ckey = (int)(pshdr->cKeyMac - ikey); Assert(pghidskey); if (ckey) memmove((LPBYTE)pghidskey + (ikey+1)*sizeof(GHID), (LPBYTE)pghidskey + ikey*sizeof(GHID), ckey*sizeof(GHID)); pghidskey[ikey] = GH_GetId(hgh, pskey); ++(pshdr->cKeyMac); } else { // The key already exists.
// Chain the new reg onto it.
pghidskey = (PGHID)GH_GetPv(hgh, pshdr->ghidKeyList); pskey = (LPSKEY)GH_GetPv(hgh, pghidskey[ikey]); } sc = S_OK;
// Hook reg info to key, and place back pointer to the key in
// the reg info.
psreg->ghidRegNext = pskey->ghidRegs; pskey->ghidRegs = ghidsreg; ++(pskey->cRef); psreg->ghidKey = GH_GetId(hgh, pskey);
#ifdef DEBUG
if (fAlwaysValidateKeys) Assert(FValidRgkey(hgh, pshdr)); #endif
*lpulConnection = (ULONG)ghidsreg;
ret: if (sc) { if (pskey && !pskey->cRef) GH_Free(hgh, GH_GetId(hgh, pskey)); if (psreg) GH_Free(hgh, ghidsreg); if (fCleanupAdviseList) (void) ScDelAdviseList(*lppList, (ULONG)ghidsreg); }
DebugTraceSc(ScSubscribe, sc); return sc;
oom: sc = MAPI_E_NOT_ENOUGH_MEMORY; goto ret; }
STDMETHODIMP HrUnsubscribe(LPADVISELIST FAR *lppList, ULONG ulConnection) { SCODE sc; LPINST pinst = NULL; HGH hgh = NULL; LPSREG psreg; LPSHDR pshdr = NULL; LPMAPIADVISESINK padvise = NULL; BOOL fSinkBusy;
#ifdef PARAMETER_VALIDATION
if (IsBadWritePtr(lppList, sizeof(LPADVISELIST))) { DebugTraceArg(HrUnsubscribe, "lppList fails address check"); return ResultFromScode(E_INVALIDARG); } if (*lppList && IsBadWritePtr(*lppList, offsetof(ADVISELIST, rgItems))) { DebugTraceArg(HrUnsubscribe, "*lppList fails address check"); return ResultFromScode(E_INVALIDARG); } #endif /* PARAMETER_VALIDATION */
if (sc = ScGetInst(&pinst)) goto ret;
hgh = pinst->hghShared;
if (!GH_WaitForMutex(hgh, INFINITE)) { sc = MAPI_E_TIMEOUT; goto ret; }
pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr);
if (!FValidReg(hgh, pshdr, (GHID)ulConnection)) { DebugTraceArg(HrUnsubscribe, "ulConnection refers to invalid memory"); goto badReg; }
psreg = (LPSREG)GH_GetPv(hgh, (GHID)ulConnection);
#ifdef DEBUG
if (fAlwaysValidateKeys) Assert(FValidRgkey(hgh, pshdr)); #endif
psreg->ulFlagsAndRefcount |= SREG_DELETING; fSinkBusy = IsRefCallback(psreg);
if (!fSinkBusy) Unregister(pinst, psreg->ghidKey, (GHID)ulConnection, &padvise);
#ifdef DEBUG
if (fAlwaysValidateKeys) Assert(FValidRgkey(hgh, pshdr)); #endif
// The advise sink should be released with nothing else held.
GH_ReleaseMutex(hgh); pshdr = NULL; ReleaseInst(&pinst);
// Drop our reference to the advise sink
if (padvise && !fSinkBusy && !FBadIUnknownComplete(padvise)) { UlRelease(padvise); }
if (!*lppList) { sc = MAPI_E_NOT_FOUND; goto ret; }
sc = ScDelAdviseList(*lppList, ulConnection);
ret: if (pshdr) GH_ReleaseMutex(hgh); ReleaseInst(&pinst); DebugTraceSc(HrUnsubscribe, sc); return ResultFromScode(sc);
badReg: sc = MAPI_E_INVALID_PARAMETER; goto ret; }
/*
* HrNotify * * Issues a callback for each event specified, if anyone has registered * for the key and event specified. * * This is really only the front half of Notify; the rest, * including the actual callback, happens in the notification * window procedure LNotifWndProc. This function uses information * stored in shared memory to determine what processes are interested * in the callback. * * lpKey Uniquely identifies the object in which * interest was registered. Both the key * and the event in the notification itself * must match in order for the callback to * fire. * cNotification Count of structures at rgNotifications * rgNotification Array of NOTIFICATION structures. Each * contains an event ID and parameters. * lpulFlags No input values defined. Output may be * NOTIFY_CANCEL, if Subscribe's caller * requested synchronous callback and the * callback function returned * CALLBACK_DISCONTINUE. * * Note that the output flags are ambiguous if more than one * notification was passed in -- you can't tell which callback * canceled. */ STDMETHODIMP HrNotify(LPNOTIFKEY lpKey, ULONG cNotification, LPNOTIFICATION rgNotification, ULONG FAR *lpulFlags) { SCODE sc; LPINST pinst; HGH hgh = NULL; LPSHDR pshdr = NULL; ULONG ikey; GHID ghidkey; LPTREG rgtreg = NULL; int itreg; int ctreg; LPSKEY pskey; LPSREG psreg; GHID ghidReg; int inotif; LRESULT lResult; GHID ghidParms = 0; LPSPARMS pparms = NULL; LPSTASK pstaskT; ULONG cb; LPBYTE pb; ULONG ul;
#ifdef PARAMETER_VALIDATION
if (IsBadReadPtr(lpKey, (size_t)CbNewNOTIFKEY(0)) || IsBadReadPtr(lpKey, (size_t)CbNOTIFKEY(lpKey))) { DebugTraceArg(HrNotify, "lpKey fails address check"); goto badArg; }
if (IsBadReadPtr(rgNotification, (UINT)cNotification*sizeof(NOTIFICATION))) { DebugTraceArg(HrNotify, "rgNotification fails address check"); goto badArg; }
// N.B. the NOTIFICATION structures are validated within this
// function, during the copy step.
if (IsBadWritePtr(lpulFlags, sizeof(ULONG))) { DebugTraceArg(HrNotify, "lpulFlags fails address check"); goto badArg; }
if (*lpulFlags & ulNotifyReservedFlags) { DebugTraceArg(HrNotify, "reserved flags used"); return ResultFromScode(MAPI_E_UNKNOWN_FLAGS); } #endif /* PARAMETER_VALIDATION */
if (sc = ScGetInst(&pinst)) goto ret;
Assert(pinst->hwndNotify); Assert(IsWindow(pinst->hwndNotify));
hgh = pinst->hghShared;
*lpulFlags = 0L;
// Validate the notification parameters.
// Also calculate their total size, so we know how big a block
// to get from shared memory.
if (sc = ScCountNotifications((int)cNotification, rgNotification, &cb)) goto ret;
// Lock shared memory
if (!GH_WaitForMutex(hgh, INFINITE)) { sc = MAPI_E_TIMEOUT; goto ret; }
pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr);
// Locate the key we're told to notify on.
sc = ScFindKey(lpKey, hgh, pshdr, &ikey); if (sc == S_FALSE) { // Nobody is registered for this key. All done.
sc = S_OK; goto ret; }
ghidkey = ((PGHID)GH_GetPv(hgh, pshdr->ghidKeyList))[ikey]; pskey = (LPSKEY)GH_GetPv(hgh, ghidkey);
// Form the logical OR of all the events we were passed. Use it
// as a shortcut to determine whether a particular registration
// triggers.
ul = 0; for (inotif = 0; inotif < (int)cNotification; ++inotif) ul |= rgNotification[inotif].ulEventType;
// Walk list of registrations and build a set of window
// handles that need to be notified. Also remember which ones
// want sync notification.
// The list of registrations may be empty if some notification messages
// are still waiting to be handled.
if (sc = STACK_ALLOC(pskey->cRef * sizeof(TREG), rgtreg)) goto ret;
itreg = 0; for (ghidReg = pskey->ghidRegs; ghidReg; ) { GHID ghidTask; HWND hwnd;
psreg = (LPSREG)GH_GetPv(hgh, ghidReg); hwnd = psreg->hwnd;
if (!IsValidTask( hwnd, pinst )) { // The task for which we created this window has died.
// Continue loop first 'cause our link is about to go away.
do { ghidReg = psreg->ghidRegNext; } while (ghidReg && (psreg = ((LPSREG)GH_GetPv(hgh, ghidReg)))->hwnd == hwnd);
// Blow away everything associated with the dead task.
CleanupTask(pinst, hwnd, TRUE);
continue; }
if (ul & psreg->ulEventMask) { // Caller wants this event. Add it to temporary list.
//
if (psreg->ulFlagsAndRefcount & SREG_DELETING) { DebugTrace("Skipping notify to reg pending deletion\n"); } else if (!ScFindTask(pinst, hwnd, hgh, &ghidTask, NULL)) { pstaskT = (LPSTASK) GH_GetPv(hgh, ghidTask);
rgtreg[itreg].hwnd = hwnd; rgtreg[itreg].fSync = ((psreg->ulFlagsAndRefcount & NOTIFY_SYNC) != 0) && ((pstaskT->uFlags & MAPI_TASK_PENDING) == 0); #ifdef DEBUG
if (((psreg->ulFlagsAndRefcount & NOTIFY_SYNC) != 0) && ((pstaskT->uFlags & MAPI_TASK_PENDING) != 0)) { DebugTrace("HrNotify: deferring sync spooler notification\n"); } #endif
rgtreg[itreg].ghidTask = ghidTask; ++itreg; pstaskT = NULL; } else TrapSz1("WARNING: %s trying to notify to a non-task", pinst->szModName); }
ghidReg = psreg->ghidRegNext; }
if (itreg == 0) { // Nobody is registered for this event. All done.
sc = S_OK; goto ret; } ctreg = itreg;
// Create the parms structure in shared memory.
if (!(ghidParms = GH_Alloc(hgh, (UINT)(cb + offsetof(SPARMS,ab[0]))))) goto oom;
pparms = (LPSPARMS)GH_GetPv(hgh, ghidParms);
// Now copy the notification parameters.
pb = pparms->ab; if (sc = ScCopyNotifications((int)cNotification, rgNotification, (LPVOID)pb, &cb)) goto ret;
// Fill in the rest of the parms structure.
pparms->cRef = 0; pparms->ghidKey = ghidkey; pparms->cNotifications = cNotification; pparms->pvRef = (LPVOID)(pparms->ab); pparms->cb = cb;
// Queue async notification to each task that's getting it.
// Sync notifications are handled separately.
for (itreg = 0; itreg < ctreg; ++itreg) { if (!rgtreg[itreg].fSync) { sc = ScEnqueueParms(pinst, hgh, rgtreg[itreg].ghidTask, ghidParms); if (FAILED(sc)) goto ret;
if (sc == S_FALSE) continue; }
pparms->cRef++; } sc = S_OK;
// Bump the reference count on the SKEY by the number of notifications
// we're going to issue.
// The registration list may change between issuance and handling of
// notifications; we handle registrations disappearing, and new ones
// appearing is not a problem.
pskey->cRef += pparms->cRef;
// Loop through the registrations. If the caller requested a
// synchronous notification, use SendMessage to invoke the callback
// and record the result. Otherwise, use PostMessage for an
// asynchronous notification.
lResult = 0; for (itreg = 0; itreg < ctreg; ++itreg) { pstaskT = (LPSTASK)GH_GetPv(hgh, rgtreg[itreg].ghidTask);
if (rgtreg[itreg].hwnd == hwndNoSpooler) { Assert(pstaskT->uFlags & MAPI_TASK_PENDING); Assert(pstaskT->uFlags & MAPI_TASK_SPOOLER); continue; }
if (rgtreg[itreg].fSync) { // Unlock shared memory. A sync callback function
// will need to access it. This invalidates 'pstaskT'.
GH_ReleaseMutex(hgh); ReleaseInst(&pinst); pshdr = NULL; pstaskT = NULL;
// Issue synchronous notification.
// Merge the results if there are multiple registrands.
lResult |= SendMessage(rgtreg[itreg].hwnd, wmNotify, (WPARAM)1, (LPARAM)ghidParms); if ((sc = ScGetInst(&pinst)) || !GH_WaitForMutex(hgh, INFINITE)) { lResult |= CALLBACK_DISCONTINUE; break; } pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr); } else { if (!pstaskT->fSignalled) { // Post asynchronous notification message.
pstaskT->fSignalled = PostMessage(rgtreg[itreg].hwnd, wmNotify, (WPARAM)0, (LPARAM)0); #ifdef DEBUG
if (!pstaskT->fSignalled) { // Queue is full. They'll just have to wait until
// a later notification.
DebugTrace("Failed to post notification message to %s\n", pstaskT->szModName); } #endif /* DEBUG */
} } // else a message has already been queued
}
if (lResult & CALLBACK_DISCONTINUE) *lpulFlags = NOTIFY_CANCELED;
ret: if (sc) { if (pparms && !pparms->cRef) GH_Free(hgh, ghidParms); } if (pshdr) GH_ReleaseMutex(hgh); ReleaseInst(&pinst); STACK_FREE(rgtreg); DebugTraceSc(HrNotify, sc); return ResultFromScode(sc);
oom: sc = MAPI_E_NOT_ENOUGH_MEMORY; goto ret;
#ifdef PARAMETER_VALIDATION
badArg: #endif
return ResultFromScode(MAPI_E_INVALID_PARAMETER); }
/* End of notification engine exported functions */
/*
* LNotifWndProc * * Notification window procedure, the back half of Notify(). * * The shared-memory offset of the notification parameter structure * (SPARM) is passed as the LPARAM of wmNotify. */
#ifndef MAC
LRESULT STDAPICALLTYPE #else
LRESULT CALLBACK #endif
LNotifWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { extern int fDaylight; // Defined in ..\common\dt.c
// Handle normal window messages.
if (msg != wmNotify) { #ifdef WIN16
if (msg == WM_TIMECHANGE) { // Reload the timezone information from WIN.INI.
fDaylight = -1; CheckTimezone(NULL); } #endif /* WIN16 */
return DefWindowProc(hwnd, msg, wParam, lParam); } return DrainFilteredNotifQueue((BOOL)wParam, (GHID)lParam, NULL); }
LRESULT STDAPICALLTYPE DrainNotifQueue(BOOL fSync, ULONG ghidParms) { return DrainFilteredNotifQueue(fSync, (GHID)ghidParms, NULL); }
LRESULT STDAPICALLTYPE DrainFilteredNotifQueue(BOOL fSync, GHID ghidParms, LPNOTIFKEY pkeyFilter) { SCODE sc; LRESULT l = 0L; LRESULT lT; LPINST pinst; HGH hgh = NULL; LPSHDR pshdr = NULL; LPSKEY pskey; GHID ghidReg; int ireg; int creg; LPSREG rgsreg = NULL; LPSREG psreg; LPSPARMS pparmsS; LPSPARMS pparms = NULL; int intf; LPNOTIFICATION pntf; #ifndef GH_POINTERS_VALID
ULONG cb; #endif
ULONG ulEvents; LPSTASK pstask = NULL; GHID ghidTask; HWND hwndNotify; HLH hlh = NULL; #ifdef DEBUG
int sum1; int sum2; LPBYTE pb; #endif
if (sc = ScGetInstRetry(&pinst)) goto ret;
hgh = pinst->hghShared; hwndNotify = pinst->hwndNotify; hlh = pinst->hlhInternal;
// Lock shared memory. It is locked here and at the bottom of the
// main loop, and unlocked in the middle of the main loop.
//$ Review: using an infinite timeout is not good.
//$ We should use a fairly short timeout (say, .2 seconds) and re-post
//$ the message if the timeout expires.
if (!GH_WaitForMutex(hgh, INFINITE)) { sc = MAPI_E_TIMEOUT; goto ret; }
pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr);
for (;;) { pstask = NULL;
if (fSync) { // Synchronous notification. Caller gave us parameters.
// Check them minimally.
#ifdef DEBUG
AssertSz(FValidParms(hgh, pshdr, ghidParms), "DrainFilteredNotif with fSync"); #endif /* DEBUG */
} else { // Async notification. Find our task queue and pull off the
// first parameter set.
if (ScFindTask(pinst, hwndNotify, hgh, &ghidTask, NULL)) { // No queue. Perhaps the message came in late. Just bail.
sc = S_OK; goto ret; } pstask = (LPSTASK)GH_GetPv(hgh, ghidTask);
if (ScDequeueParms(hgh, pstask, pkeyFilter, &ghidParms)) // Queue is empty. All finished.
break; }
// Copy registrations to local memory. We need at most
// the number of registrations on the key, so we count them.
pparmsS = (LPSPARMS)GH_GetPv(hgh, ghidParms); pskey = (LPSKEY)GH_GetPv(hgh, pparmsS->ghidKey); for (ghidReg = pskey->ghidRegs, creg = 0; ghidReg; ) { psreg = (LPSREG)GH_GetPv(hgh, ghidReg); ghidReg = psreg->ghidRegNext; ++creg; }
if (creg != 0) { rgsreg = LH_Alloc(hlh, creg * sizeof(SREG)); if (!rgsreg) goto oom; LH_SetName(hlh, rgsreg, "Copy of notification registrations");
// Copy notification parameters to local memory IF they
// need to be relocated. This is true only on NT; on other
// platforms, the global heap manager maps shared memory to
// the same address on all processes.
#ifndef GH_POINTERS_VALID
pparms = LH_Alloc(hlh, pparmsS->cb + offsetof(SPARMS, ab[0])); if (!pparms) goto oom; LH_SetName(hlh, pparms, "Copy of notification parameters"); MemCopy(pparms, pparmsS, (UINT)pparmsS->cb + offsetof(SPARMS,ab[0])); #else
pparms = pparmsS; #endif
ghidReg = pskey->ghidRegs; ireg = 0; while (ghidReg) { Assert(ireg < creg); psreg = (LPSREG)GH_GetPv(hgh, ghidReg); if (psreg->hwnd == hwndNotify) { // It's for this process, use it.
// AddRef the advise sink (sorta). The registration may
// go away after we let go of the mutexes.
if (FBadIUnknownComplete(psreg->lpAdvise) || IsBadCodePtr((FARPROC)psreg->lpAdvise->lpVtbl->OnNotify)) { DebugTrace("Notif callback 0x%lx went bad on me (1)!\n", psreg->lpAdvise); } else if (psreg->ulFlagsAndRefcount & SREG_DELETING) { DebugTrace("Skipping notif callback on disappearing advise sink\n"); } else { // Keep a reference alive to the advise sink, BUT
// without calling AddRef with stuff locked.
// UlAddRef(psreg->lpAdvise);
AddRefCallback(psreg);
MemCopy(rgsreg + ireg, psreg, sizeof(SREG));
// Keep a reference to the shared SREG.
// The callback refcount will keep it alive for us.
rgsreg[ireg].ghidRegNext = GH_GetId(hgh, psreg);
++ireg; } } ghidReg = psreg->ghidRegNext; } creg = ireg; }
// Unlock shared memory. After this point, references into
// shared memory should no longer be used, so we null them out.
GH_ReleaseMutex(hgh); pshdr = NULL; psreg = NULL; pparmsS = NULL; pstask = NULL; ReleaseInst(&pinst);
if (creg == 0) goto cleanup; // Nothing to do for this notification
pntf = (LPNOTIFICATION)pparms->ab; ulEvents = 0; for (intf = 0; (ULONG)intf < pparms->cNotifications; ++intf) { ulEvents |= pntf[intf].ulEventType; } #ifndef GH_POINTERS_VALID
// Adjust the pointers within the notification parameters. Yeech.
if (sc = ScRelocNotifications((int)pparms->cNotifications, (LPNOTIFICATION)pparms->ab, pparms->pvRef, pparms->ab, &cb)) goto ret; #endif
#ifdef DEBUG
// Checksum the notification. We'll assert if the callback
// function modifies it.
sum1 = 0; for (pb = pparms->ab + pparms->cb - 1; pb >= pparms->ab; --pb) sum1 += *pb; #endif
// Loop through the list of registrations. For each one,
// issue the callback if it is of interest.
// Remember the result for synchronous callbacks.
pntf = (LPNOTIFICATION)pparms->ab; #if defined (_AMD64_) || defined(_IA64_)
AssertSz (FIsAligned (pparms->ab), "DrainFilteredNotifyQueue: unaligned reloceated notif"); #endif
for (ireg = creg, psreg = rgsreg; ireg--; ++psreg) { if ((ulEvents & psreg->ulEventMask)) { if (FBadIUnknownComplete(psreg->lpAdvise) || IsBadCodePtr((FARPROC)psreg->lpAdvise->lpVtbl->OnNotify)) { DebugTrace("Notif callback 0x%lx went bad on me (2)!\n", psreg->lpAdvise); continue; }
// Issue the callback
lT = psreg->lpAdvise->lpVtbl->OnNotify(psreg->lpAdvise, pparms->cNotifications, pntf);
// Record the result. Stop issuing notifications if so asked.
// Note that this does not stop handling of other events,
// i.e. we do not break the outer loop.
if (psreg->ulFlagsAndRefcount & NOTIFY_SYNC) { // Assert(fSync);
// or the task is still marked pending
l |= lT; if (lT == CALLBACK_DISCONTINUE) break; #ifdef DEBUG
else if (lT) DebugTrace("DrainNotifQueue: callback function returns garbage 0x%lx\n", lT); #endif
}
#ifdef DEBUG
// Checksum the notification again, and assert if the
// callback function has modified it.
sum2 = 0; for (pb = pparms->ab + pparms->cb - 1; pb >= pparms->ab; --pb) sum2 += *pb; AssertSz(sum1 == sum2, "Notification callback modified its parameters"); #endif
} }
cleanup: Assert(ghidParms);
// Lock shared memory
if (sc = ScGetInstRetry(&pinst)) goto ret; if (!GH_WaitForMutex(hgh, INFINITE)) { sc = MAPI_E_TIMEOUT; goto ret; } pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr);
// Deref the callback and see if we need to release any
// advise sinks. If somebody unadvised while we were calling back,
// the SREG_DELETING flag is now on.
for (ireg = creg, psreg = rgsreg; ireg--; ++psreg) { LPSREG psregT;
Assert(FValidReg(hgh, pshdr, psreg->ghidRegNext)); psregT = (LPSREG) GH_GetPv(hgh, psreg->ghidRegNext);
// Deref the callback in lieu of actually releasing
// the advise sink.
ReleaseCallback(psregT); if ((psregT->ulFlagsAndRefcount & SREG_DELETING) && !IsRefCallback(psregT)) { DebugTrace("Unadvise happened during my callback\n"); psreg->ulFlagsAndRefcount |= SREG_DELETING;
Unregister(pinst, psregT->ghidKey, psreg->ghidRegNext, NULL); }
// if (FBadIUnknownComplete(psreg->lpAdvise) ||
// IsBadCodePtr((FARPROC)psreg->lpAdvise->lpVtbl->OnNotify))
// {
// DebugTrace("Notif callback 0x%lx went bad on me (3)!\n", psreg->lpAdvise);
// continue;
// }
//
// UlRelease(psreg->lpAdvise);
}
// Verify that the parms pointer is still good. It may have
// been cleaned up when we let go of everything.
//$ That should only happen if the whole engine goes away.
if (FValidParms(hgh, pshdr, ghidParms)) { // Note: pparms may be NULL at this point
pparmsS = (LPSPARMS)GH_GetPv(hgh, ghidParms);
// Let go of the key. If the parms are still valid,
// so should the key be -- it's validated in FValidParms.
pskey = (LPSKEY)GH_GetPv(hgh, pparmsS->ghidKey); Assert(!pparms || pparms->ghidKey == pparmsS->ghidKey); ReleaseKey(pinst, pparmsS->ghidKey);
// Let go of the parameters
if (--(pparmsS->cRef) == 0) GH_Free(hgh, ghidParms); } #ifdef DEBUG
else DebugTrace("DrainFilteredNotif cleanup: parms %08lx are gone\n", ghidParms); #endif
pparmsS = NULL; ghidParms = 0;
#ifndef GH_POINTERS_VALID
if (pparms) LH_Free(hlh, pparms); #endif
pparms = NULL;
// Let go of resources again, and release any advise sinks that
// may need to be released.
GH_ReleaseMutex(hgh); pshdr = NULL; ReleaseInst(&pinst);
for (ireg = creg, psreg = rgsreg; ireg--; ++psreg) { if (psreg->ulFlagsAndRefcount & SREG_DELETING) { if (FBadIUnknownComplete(psreg->lpAdvise) || IsBadCodePtr((FARPROC)psreg->lpAdvise->lpVtbl->OnNotify)) { DebugTrace("Notif callback 0x%lx went bad on me (3)!\n", psreg->lpAdvise); continue; }
UlRelease(psreg->lpAdvise); } }
if (rgsreg) LH_Free(hlh, rgsreg); rgsreg = NULL;
// If we handled a sync notification, do not loop back.
if (fSync) break;
// Lock shared memory for next iteration of the loop
if (sc = ScGetInstRetry(&pinst)) goto ret; if (!GH_WaitForMutex(hgh, INFINITE)) { sc = MAPI_E_TIMEOUT; goto ret; } pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr); }
ret: // Decrement the reference counter on the notification parameters.
// Free them if it drops to 0.
if (ghidParms) { // Lock instance and shared memory
if (pinst || !(sc = ScGetInstRetry(&pinst))) { if (pshdr || GH_WaitForMutex(hgh, INFINITE)) { pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr);
// Verify that the parms are still there
pparmsS = (LPSPARMS)GH_GetPv(hgh, ghidParms);
// Let go of the key
pskey = (LPSKEY)GH_GetPv(hgh, pparmsS->ghidKey); Assert(!pparms || pparms->ghidKey == pparmsS->ghidKey); ReleaseKey(pinst, pparmsS->ghidKey);
// Let go of the parameters
if (--(pparmsS->cRef) == 0) GH_Free(hgh, ghidParms); } } }
if (pshdr) GH_ReleaseMutex(hgh);
#ifndef GH_POINTERS_VALID
if (pparms) LH_Free(hlh, pparms); #endif
if (rgsreg) LH_Free(hlh, rgsreg); ReleaseInst(&pinst); #ifdef DEBUG
if (sc) { DebugTrace("DrainNotifQueue failed to handle notification (%s)\n", SzDecodeScode(sc)); } #endif
return l;
oom: sc = MAPI_E_NOT_ENOUGH_MEMORY; goto ret; }
// See mapidbg.h for similar macros.
#define TraceIfSz(t,psz) IFTRACE((t) ? DebugTraceFn("~" psz) : 0)
BOOL FBadIUnknownComplete(LPVOID lpv) { BOOL fBad; LPUNKNOWN lpObj = (LPUNKNOWN) lpv;
fBad = IsBadReadPtr(lpObj, sizeof(LPVOID)); TraceIfSz(fBad, "FBadIUnknownComplete: object bad");
if (!fBad) { fBad = IsBadReadPtr(lpObj->lpVtbl, 3 * sizeof(LPUNKNOWN)); TraceIfSz(fBad, "FBadIUnknownComplete: vtbl bad"); }
if (!fBad) { fBad = IsBadCodePtr((FARPROC)lpObj->lpVtbl->QueryInterface); TraceIfSz(fBad, "FBadIUnknownComplete: QI bad"); }
if (!fBad) { fBad = IsBadCodePtr((FARPROC)lpObj->lpVtbl->AddRef); TraceIfSz(fBad, "FBadIUnknownComplete: AddRef bad"); }
if (!fBad) { fBad = IsBadCodePtr((FARPROC)lpObj->lpVtbl->Release); TraceIfSz(fBad, "FBadIUnknownComplete: Release bad"); }
return fBad; }
#undef TraceIfSz
#ifdef WIN16
SCODE ScGetInstRetry(LPINST FAR *ppinst) { LPINST pinst = (LPINST)PvGetInstanceGlobals(); DWORD dwPid;
Assert(ppinst); *ppinst = NULL;
if (!pinst) { // We may have gotten called with an unusual value of SS,
// which is normally our search key for instance globals.
// Retry using the OLE process ID.
dwPid = CoGetCurrentProcess(); pinst = PvSlowGetInstanceGlobals(dwPid);
if (!pinst) { DebugTraceSc(ScGetInst, MAPI_E_NOT_INITIALIZED); return MAPI_E_NOT_INITIALIZED; } } if (!pinst->cRef) { DebugTrace("ScGetInst: race! cRef == 0 before EnterCriticalSection\r\n"); return MAPI_E_NOT_INITIALIZED; }
EnterCriticalSection(&pinst->cs);
if (!pinst->cRef) { DebugTrace("ScGetInst: race! cRef == 0 after EnterCriticalSection\r\n"); LeaveCriticalSection(&pinst->cs); return MAPI_E_NOT_INITIALIZED; }
*ppinst = pinst; return S_OK; }
#endif /* WIN16 */
/*
* ScFindKey * * Searches for a notification key in the shared memory list. * The list is sorted descending by notification key. * * Returns: * S_OK: key was found * S_FALSE: key was not found * in EITHER case, *pikey is an index into the key list, which points * to the first entry >= lpKey. */ SCODE ScFindKey(LPNOTIFKEY pkey, HGH hgh, LPSHDR pshdr, ULONG FAR *pikey) { ULONG ikey; PGHID pghidKey; int ckey; UINT cbT; int n = -1; LPNOTIFKEY pkeyT;
Assert(pkey->cb <= 0xFFFF);
//$ SPEED try binary search ?
ikey = 0; ckey = pshdr->cKeyMac;
if (pshdr->ghidKeyList) { pghidKey = (PGHID)GH_GetPv(hgh, pshdr->ghidKeyList);
while (ckey--) { pkeyT = &((LPSKEY)GH_GetPv(hgh, pghidKey[ikey]))->key; cbT = min((UINT)pkey->cb, (UINT)pkeyT->cb); n = memcmp(pkey->ab, pkeyT->ab, cbT); if (n == 0 && pkey->cb != pkeyT->cb) n = pkey->cb > pkeyT->cb ? 1 : -1;
if (n >= 0) break; ++ikey; } }
*pikey = ikey; return n == 0 ? S_OK : S_FALSE; }
/*
* Unregister * * Removes a registration structure (SREG) from shared memory. If * that was the last registration for its key, also removes the * key. * * Hooked to notification object release and invalidation. */ void Unregister(LPINST pinst, GHID ghidKey, GHID ghidReg, LPMAPIADVISESINK FAR *ppadvise) { HGH hgh = pinst->hghShared; LPSHDR pshdr; LPSKEY pskey; LPSREG psreg; GHID ghid; GHID ghidPrev;
pshdr = GH_GetPv(hgh, pinst->ghidshdr);
// Note: validation of the SREG and SKEY structures is assumed to
// have been done before calling this routine. We just assert it.
pskey = (LPSKEY)GH_GetPv(hgh, ghidKey); Assert(FValidKey(hgh, pshdr, ghidKey));
// Remove the SREG structure from the list and free it
for (ghid = pskey->ghidRegs, ghidPrev = 0; ghid; ghidPrev = ghid, ghid = ((LPSREG)GH_GetPv(hgh, ghid))->ghidRegNext) { if (ghid == ghidReg) { psreg = (LPSREG)GH_GetPv(hgh, ghid);
if (ghidPrev) ((LPSREG)GH_GetPv(hgh, ghidPrev))->ghidRegNext = psreg->ghidRegNext; else pskey->ghidRegs = psreg->ghidRegNext;
if (ppadvise) *ppadvise = psreg->lpAdvise;
GH_Free(hgh, ghid); break; } }
ReleaseKey(pinst, ghidKey); }
void ReleaseKey(LPINST pinst, GHID ghidKey) { HGH hgh; LPSHDR pshdr; LPSKEY pskey; PGHID pghid; int cghid;
Assert(ghidKey); hgh = pinst->hghShared; pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr); pskey = (LPSKEY)GH_GetPv(hgh, ghidKey);
// Decrement the key's refcount, and free the key if it's now 0
if (--(pskey->cRef) == 0) { Assert(pskey->ghidRegs == 0);
pghid = (PGHID)GH_GetPv(hgh, pshdr->ghidKeyList); cghid = pshdr->cKeyMac;
for ( ; cghid--; ++pghid) { if (*pghid == ghidKey) { // tricky: cghid already decremented in the loop test
MemCopy(pghid, (LPBYTE)pghid + sizeof(GHID), cghid*sizeof(GHID)); --(pshdr->cKeyMac); GH_Free(hgh, ghidKey); break; } } } }
BOOL FValidKey(HGH hgh, LPSHDR pshdr, GHID ghidKey) { int cKey; GHID * pghidKey; LPSREG psreg; LPSKEY pskey; GHID ghidregT; int creg;
// Check for accessible memory.
// GH doesn't expose the ability to check whether it's a
// valid block in the heap.
if (IsBadWritePtr(GH_GetPv(hgh, ghidKey), sizeof(SKEY))) { DebugTraceArg(FValidKey, "key is not valid memory"); return FALSE; }
// Verify that the key is in the list of all keys.
Assert(pshdr->cKeyMac < 0x10000); cKey = (int) pshdr->cKeyMac; pghidKey = (PGHID)GH_GetPv(hgh, pshdr->ghidKeyList); for ( ; cKey > 0; --cKey, ++pghidKey) { if (ghidKey == *pghidKey) break; } // If we fell off the loop, the key is missing
if (cKey <= 0) { DebugTraceArg(FValidKey, "key not found in shared header list"); return FALSE; }
// Validate the registration chain.
pskey = (LPSKEY)GH_GetPv(hgh, ghidKey); creg = 0; for (ghidregT = pskey->ghidRegs; ghidregT; ) { ++creg;
psreg = (LPSREG) GH_GetPv(hgh, ghidregT); if (IsBadWritePtr(psreg, sizeof(SREG))) { DebugTraceArg(FValidReg, "key has broken reg chain"); return FALSE; } if (psreg->ghidKey != ghidKey) { DebugTraceArg(FValidReg, "key has broken or crossed reg chains"); return FALSE; } if (creg > pskey->cRef) { // FWIW, this will also catch a cycle
DebugTraceArg(FValidReg, "ghidReg's key chain length exceeds refcount"); return FALSE; }
ghidregT = psreg->ghidRegNext; }
return TRUE; }
BOOL FValidReg(HGH hgh, LPSHDR pshdr, GHID ghidReg) { LPSREG psreg; LPSKEY pskey; GHID ghidregT; GHID ghidKey; UINT creg = 0;
// Check for accessible memory.
// GH does not expose the ability to check whether it's a
// valid block in the heap.
psreg = (LPSREG)GH_GetPv(hgh, ghidReg); if ( IsBadWritePtr(psreg, sizeof(SREG)) #if defined (_AMD64_) || defined(_IA64_)
|| !FIsAligned(psreg) #endif
) { DebugTraceArg(FValidReg, "ghidReg refers to invalid memory"); return FALSE; }
// Validate the key.
ghidKey = psreg->ghidKey; if (!FValidKey(hgh, pshdr, ghidKey)) { DebugTraceArg(FValidReg, "ghidReg contains an invalid key"); return FALSE; }
// FValidKey validated the key's registration chain, so we can
// now safely loop through and check for this registration.
pskey = (LPSKEY)GH_GetPv(hgh, ghidKey); for (ghidregT = pskey->ghidRegs; ghidregT; ) { if (ghidReg == ghidregT) return TRUE;
psreg = (LPSREG) GH_GetPv(hgh, ghidregT); ghidregT = psreg->ghidRegNext; }
// If we fell off the loop, the registration is missing
DebugTraceArg(FValidReg, "ghidReg is not linked to its key"); return FALSE; }
BOOL FValidParms(HGH hgh, LPSHDR pshdr, GHID ghidParms) { LPSPARMS pparms;
pparms = (LPSPARMS)GH_GetPv(hgh, ghidParms); if (IsBadWritePtr(pparms, offsetof(SPARMS, ab)) || IsBadWritePtr(pparms, (UINT) (offsetof(SPARMS, ab) + pparms->cb))) { DebugTraceArg(FValidParms, "ghidParms refers to invalid memory"); return FALSE; }
if (!FValidKey(hgh, pshdr, pparms->ghidKey)) { DebugTraceArg(FValidParms, "ghidParms does not contain a valid key"); return FALSE; }
//$ Notification parameters not checked
return TRUE; }
BOOL FValidRgkey(HGH hgh, LPSHDR pshdr) { PGHID pghidKey; UINT cKey;
cKey = (UINT) pshdr->cKeyMac; pghidKey = (PGHID)GH_GetPv(hgh, pshdr->ghidKeyList); if (cKey == 0) return TRUE;
// Address-check the list of keys
if (IsBadWritePtr(pghidKey, cKey*sizeof(GHID))) { DebugTraceArg(FValidRgkey, "key list is toast"); return FALSE; }
// Validate each key in the list
for ( ; cKey; --cKey, ++pghidKey) { if (!FValidKey(hgh, pshdr, *pghidKey)) { DebugTrace("FValidRgkey: element %d of %d (value 0x%08lx) is bad\n", (UINT)pshdr->cKeyMac - cKey, (UINT)pshdr->cKeyMac, *pghidKey); return FALSE; } }
// Verify that the NOTIFKEYs are in the right order
if (!FSortedRgkey(hgh, pshdr)) { DebugTraceArg(FValidRgkey, "key list is out of order"); return FALSE; }
return TRUE; }
BOOL FSortedRgkey(HGH hgh, LPSHDR pshdr) { PGHID pghidKey; UINT cKey; LPSKEY pskey1; LPSKEY pskey2; UINT cb; int n;
cKey = (UINT) pshdr->cKeyMac; if (cKey < 1) return TRUE;
pghidKey = (PGHID)GH_GetPv(hgh, pshdr->ghidKeyList); for (--cKey; cKey > 0; --cKey, ++pghidKey) { pskey1 = (LPSKEY)GH_GetPv(hgh, pghidKey[0]); pskey2 = (LPSKEY)GH_GetPv(hgh, pghidKey[1]); cb = (UINT) min(pskey1->key.cb, pskey2->key.cb); n = memcmp(pskey1->key.ab, pskey2->key.ab, cb); if (n < 0 || (n == 0 && pskey1->key.cb < pskey2->key.cb)) return FALSE; }
return TRUE; }
SCODE ScFindTask(LPINST pinst, HWND hwndNotify, HGH hgh, PGHID pghidTask, PGHID pghidTaskPrev) { LPSHDR pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr); GHID ghidTask; GHID ghidTaskPrev = 0; LPSTASK pstask;
for (ghidTask = pshdr->ghidTaskList; ghidTask; ghidTask = pstask->ghidTaskNext) { pstask = (LPSTASK)GH_GetPv(hgh, ghidTask); if (pstask->hwndNotify == hwndNotify) goto found; else if (hwndNotify == hwndNoSpooler && (pstask->uFlags & MAPI_TASK_SPOOLER)) { Assert(pstask->uFlags & MAPI_TASK_PENDING); TraceSz1("ScFindTask: %s hit spooler startup window", pinst->szModName); goto found; }
ghidTaskPrev = ghidTask; }
DebugTraceSc(ScFindTask, S_FALSE); return S_FALSE;
found: *pghidTask = ghidTask; if (pghidTaskPrev) *pghidTaskPrev = ghidTaskPrev;
return S_OK; }
/*
* All necessary locks must be acquired before calling this function. */ void CleanupTask(LPINST pinst, HWND hwndNotify, BOOL fGripe) { HGH hgh = pinst->hghShared; LPSHDR pshdr; GHID ghidTask; GHID ghidTaskPrev; LPSTASK pstask; PGHID rgghid; UINT ighid; GHID ghidKey; LPSKEY pskey; LPSPARMS pparms; GHID ghidReg; GHID ghidRegNext; LPSREG psreg; USHORT ckey;
pshdr = (LPSHDR)GH_GetPv(hgh, pinst->ghidshdr);
// Locate task
if (ScFindTask(pinst, hwndNotify, hgh, &ghidTask, &ghidTaskPrev)) return;
pstask = (LPSTASK)GH_GetPv(hgh, ghidTask);
#ifdef DEBUG
#if 1
// The message box will now give us problems internally -- since
// we're holding the shared memory mutex and other callers will now
// time out and error instead of waiting indefinitely.
if (fGripe) DebugTrace("Notification client \'%s\' exited without cleaning up\n", pstask->szModName); #else
// Note: system modal box rather than assert -- prevents race condition
// that kills EMS notification distribution.
if (fGripe) { CHAR szErr[128]; HWND hwnd = NULL;
#ifdef WIN32
hwnd = GetActiveWindow(); #endif
wnsprintfA(szErr, ARRAYSIZE(szErr), "Notification client \'%s\' exited without cleaning up\n", pstask->szModName); MessageBoxA(hwnd, szErr, "MAPI 1.0", MB_SYSTEMMODAL | MB_ICONHAND | MB_OK); } #endif
#endif
// Clean up notification parameters
if (pstask->cparmsMac) { rgghid = (PGHID)GH_GetPv(hgh, pstask->ghidparms); for (ighid = 0; ighid < (UINT)pstask->cparmsMac; ++ighid) { // Free each parameter structure, and deref its key
pparms = (LPSPARMS)GH_GetPv(hgh, rgghid[ighid]); ghidKey = pparms->ghidKey; if (--(pparms->cRef) == 0) GH_Free(hgh, rgghid[ighid]); ReleaseKey(pinst, ghidKey); } }
// Clean up registrations
rgghid = (PGHID)GH_GetPv(hgh, pshdr->ghidKeyList); for (ighid = 0; ighid < (UINT)pshdr->cKeyMac; ) { ckey = pshdr->cKeyMac;
pskey = (LPSKEY)GH_GetPv(hgh, rgghid[ighid]); Assert(!IsBadWritePtr(pskey, sizeof(SKEY))); for (ghidReg = pskey->ghidRegs; ghidReg; ghidReg = ghidRegNext) { LPMAPIADVISESINK padvise = NULL;
psreg = (LPSREG)GH_GetPv(hgh, ghidReg);
if (IsBadWritePtr(psreg, sizeof(SREG))) { TrapSz1("Bad psreg == %08lX", psreg); break; }
ghidRegNext = psreg->ghidRegNext; if (psreg->hwnd == hwndNotify) // Release the registration's advise sink
// only if we're in the same process.
Unregister(pinst, rgghid[ighid], ghidReg, pinst->hwndNotify == hwndNotify ? &padvise : NULL);
if (padvise && !FBadIUnknownComplete(padvise)) UlRelease(padvise); }
// Bump index iff the key we just iterated on was not deleted
if (ckey == pshdr->cKeyMac) ++ighid; }
// Unhook and destroy the task structure
if (ghidTaskPrev) ((LPSTASK)GH_GetPv(hgh, ghidTaskPrev))->ghidTaskNext = pstask->ghidTaskNext; else pshdr->ghidTaskList = pstask->ghidTaskNext; if (pstask->ghidparms) GH_Free(hgh, pstask->ghidparms); GH_Free(hgh, ghidTask); }
/*
* Adds a parameter block index to the queue for a task. If the * parameter block is already in the queue, does not add it again. * Returns: * S_OK the item was added * S_FALSE the item was duplicate; not added * other out of memory */ SCODE ScEnqueueParms(LPINST pinst, HGH hgh, GHID ghidTask, GHID ghidParms) { SCODE sc = S_OK; int ighid; GHID ghid; PGHID rgghid; LPSTASK pstask = (LPSTASK)GH_GetPv(hgh, ghidTask);
// Make sure there's room to accommodate the new entry
if (!pstask->cparmsMax) { ghid = GH_Alloc(hgh, 8*sizeof(GHID));
if (!ghid) goto oom;
pstask->cparmsMax = 8; pstask->cparmsMac = 0; pstask->ghidparms = ghid; } else if (pstask->cparmsMac == pstask->cparmsMax) { ghid = GH_Realloc(hgh, pstask->ghidparms, (pstask->cparmsMax+8) * sizeof(GHID));
if (!ghid) { DebugTrace( "ScEnqueueParms: ghidparms can't grow.\n"); goto oom; }
pstask->cparmsMax += 8; pstask->ghidparms = ghid; } else ghid = pstask->ghidparms;
rgghid = (PGHID)GH_GetPv(hgh, ghid);
// Mark the task as needing to be signalled
if (pstask->cparmsMac == 0) pstask->fSignalled = FALSE;
// Scan for duplicates. If this entry is already in the queue,
// don't add it again; we'll scan for registrations and distribute
// the notifications on the receiving side.
for (ighid = (int)pstask->cparmsMac; ighid > 0; ) { if (rgghid[--ighid] == ghidParms) return S_FALSE; // don't trace this
}
// Add the new entry
rgghid[pstask->cparmsMac++] = ghidParms;
ret: DebugTraceSc(ScEnqueueParms, sc); return sc;
oom: sc = MAPI_E_NOT_ENOUGH_MEMORY; goto ret; }
SCODE ScDequeueParms(HGH hgh, LPSTASK pstask, LPNOTIFKEY pkeyFilter, PGHID pghidParms) { PGHID rgghid; UINT ighid; LPSKEY pskey; LPSPARMS pparmsS;
*pghidParms = 0;
if (pstask->cparmsMac == 0) { pstask->fSignalled = FALSE; if (pstask->cparmsMax > 8) { GH_Free(hgh, pstask->ghidparms); pstask->ghidparms = 0; pstask->cparmsMax = pstask->cparmsMac = 0; } return S_FALSE; }
Assert(pstask->ghidparms); rgghid = (PGHID)GH_GetPv(hgh, pstask->ghidparms); for (ighid = 0; ighid < pstask->cparmsMac; ighid++) { pparmsS = (LPSPARMS)GH_GetPv(hgh, rgghid[ighid]); pskey = (LPSKEY)GH_GetPv(hgh, pparmsS->ghidKey);
if (!pkeyFilter || ((pkeyFilter->cb == pskey->key.cb) && (!memcmp (pkeyFilter->ab, pskey->key.ab, (UINT)pskey->key.cb)))) { *pghidParms = rgghid[ighid]; MemCopy (&rgghid[ighid], &rgghid[ighid+1], (--(pstask->cparmsMac) - ighid) * sizeof(GHID));
// If we've drained the queue of pending notifications,
// flip over to normal
if ((pstask->uFlags & MAPI_TASK_PENDING) && pstask->cparmsMac == 0) { DebugTrace("ScDequeueParms: spooler is no longer pending\n"); pstask->uFlags &= ~MAPI_TASK_PENDING; }
return S_OK; } } pstask->fSignalled = FALSE; return S_FALSE; }
#if defined(WIN32) && !defined(MAC)
/*
* On NT and Windows 95, the message pump for the notification window runs * in its own thread. This is it. */ DWORD WINAPI NotifyThreadFn(DWORD dw) { MSG msg; SCODE sc; SCODE scCo; LPINST pinst = (LPINST) dw;
// SNEAKY: When ScInitMapiX spawns us, it has the pinst (but not the
// shared memory block) locked. Immediately afterward, it blocks
// until we release it. This makes it safe for us to use the pinst
// that it gives us.
scCo = CoInitialize(NULL); if (scCo) DebugTrace("NotifyThreadFn: CoInitializeEx returns %s\n", SzDecodeScode(scCo));
Assert(!IsBadWritePtr(pinst, sizeof(INST)));
if (!GH_WaitForMutex(pinst->hghShared, INFINITE)) { sc = MAPI_E_TIMEOUT; DebugTrace("NotifyThreadFn: Failed to get Global Heap Mutex.\n"); goto fail; }
sc = ScInitNotify( pinst );
if (FAILED(sc)) { GH_ReleaseMutex(pinst->hghShared); DebugTrace("NotifyThreadFn: Failed to ScInitNotify.\n"); goto fail; }
// Indicate success and unblock the spawning thread
GH_ReleaseMutex(pinst->hghShared); pinst->scInitNotify = S_OK; SetEvent(pinst->heventNotify);
// NOTE!! pinst cannot be used beyond this point.
// Run the message pump for notification.
//$ Note: this can easily be converted to waiting on an event
// or other cross-process synchronization mechanism.
while (GetMessage(&msg, NULL, 0, 0)) { // TranslateMessage() is unnecessary since we never process
// the keyboard.
DispatchMessage(&msg); }
// DeinitNotify() handles its own locking chores.
DeinitNotify();
if (SUCCEEDED(scCo)) CoUninitialize();
return 0L;
fail: // Indicate failure and unblock the spawning thread
pinst->scInitNotify = sc; SetEvent(pinst->heventNotify);
if (SUCCEEDED(scCo)) CoUninitialize();
DebugTraceSc(NotifyThreadFn, sc); return (DWORD) sc; }
#endif /* WIN32 && !MAC */
//---------------------------------------------------------------------------
// Name: IsValidTask()
// Description:
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
BOOL IsValidTask( HWND hwnd, LPINST pinst ) { #ifdef NT
GHID ghidTask; LPSTASK pstask;
if ( !ScFindTask( pinst, hwnd, pinst->hghShared, &ghidTask, NULL ) ) { pstask = (LPSTASK)GH_GetPv( pinst->hghShared, ghidTask );
if ( pstask->uFlags & MAPI_TASK_SERVICE ) { HANDLE hProc;
Assert( pstask->dwPID ); hProc = OpenProcess( PROCESS_ALL_ACCESS, 0, pstask->dwPID ); if ( hProc ) { CloseHandle( hProc ); return TRUE ; } return FALSE ; } else if ( pstask->uFlags & MAPI_TASK_PENDING ) { // Assert( hwnd == hwndNoSpooler );
return TRUE; } else { return IsWindow( hwnd ); } } else { return FALSE; } #else
return IsWindow( hwnd ) || hwnd == hwndNoSpooler; #endif
}
SCODE ScNewStask(HWND hwnd, LPSTR szTask, ULONG ulFlags, HGH hgh, LPSHDR pshdr) { SCODE sc = S_OK; GHID ghidstask; LPSTASK pstask;
if (!(ghidstask = GH_Alloc(hgh, sizeof(STASK)))) { sc = MAPI_E_NOT_ENOUGH_MEMORY; goto ret; }
pstask = (LPSTASK)GH_GetPv(hgh, ghidstask); ZeroMemory(pstask, sizeof(STASK));
pstask->hwndNotify = hwnd; StrCpyNA(pstask->szModName, szTask, sizeof(pstask->szModName));
// Set task flags
if (ulFlags & MAPI_SPOOLER_INIT) pstask->uFlags |= MAPI_TASK_SPOOLER; if (hwnd == hwndNoSpooler) pstask->uFlags |= MAPI_TASK_PENDING; #ifdef _WINNT
if ( ulFlags & MAPI_NT_SERVICE ) { pstask->uFlags |= MAPI_TASK_SERVICE; pstask->dwPID = GetCurrentProcessId(); } #endif
// Hook to task list
pstask->ghidTaskNext = pshdr->ghidTaskList; pshdr->ghidTaskList = ghidstask;
ret: DebugTraceSc(ScNewStask, sc); return sc; }
SCODE ScNewStubReg(LPINST pinst, LPSHDR pshdr, HGH hgh) { SCODE sc; ULONG ulCon; LPSREG psreg;
if (pshdr->ulConnectStub != 0) { DebugTrace("ScNewStubReg: that was fast!\n"); return S_OK; }
sc = ScSubscribe(pinst, hgh, pshdr, NULL, (LPNOTIFKEY) ¬ifkeyOlaf, fnevSpooler, NULL, 0, &ulCon); if (sc) goto ret; pshdr->ulConnectStub = ulCon; psreg = (LPSREG) GH_GetPv(hgh, (GHID) ulCon); psreg->hwnd = hwndNoSpooler;
ret: DebugTraceSc(ScNewStubReg, sc); return sc; }
VOID DeleteStubReg(LPINST pinst, LPSHDR pshdr, HGH hgh) { LPMAPIADVISESINK padvise; GHID ghidReg; LPSREG psreg;
ghidReg = (GHID) pshdr->ulConnectStub; if (!ghidReg) return;
psreg = (LPSREG)GH_GetPv(hgh, ghidReg); if (!FValidReg(hgh, pshdr, ghidReg)) { AssertSz(FALSE, "DeleteStubReg: bogus pshdr->ulConnectStub"); return; }
DebugTrace("DeleteStubReg: removing stub spooler registration\n"); Unregister(pinst, psreg->ghidKey, ghidReg, &padvise); pshdr->ulConnectStub = 0; Assert(!padvise); }
|