You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2693 lines
66 KiB
2693 lines
66 KiB
/*
|
|
* 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);
|
|
}
|