/*
 *    MSGFILTR.C
 *
 *    This file contains a standard implementation of IMessageFilter
 *    interface.
 *    This file is part of the OLE 2.0 User Interface support library.
 *
 *    (c) Copyright Microsoft Corp. 1990 - 1992 All Rights Reserved
 *
 */


#define STRICT  1
#include "ole2ui.h"
#include "msgfiltr.h"

OLEDBGDATA


typedef struct tagOLESTDMESSAGEFILTER {
	IMessageFilterVtbl FAR* m_lpVtbl;
	UINT                    m_cRef;
	HWND                    m_hWndParent;
	DWORD                   m_dwInComingCallStatus; // Status to return from
													// HandleIncomingCall
	HANDLEINCOMINGCALLBACKPROC m_lpfnHandleInComingCallback;
													// Callback function
													// to selectively handle
													// interface method calls
	BOOL                    m_fEnableBusyDialog;    // enable RetryRejected
													//  Call dialog
	BOOL                    m_fEnableNotRespondingDialog; // enable
													// MessagePending dialog
	MSGPENDINGPROC          m_lpfnMessagePendingCallback; // MessagePending
													// Callback function
	LPFNOLEUIHOOK           m_lpfnBusyDialogHookCallback; // Busy dialog hook
	LPSTR                   m_lpszAppName;          // Name of application
													// installing filter
	HWND                    m_hWndBusyDialog;       // HWND of busy dialog.  Used
													// to tear down dialog.
	BOOL                    m_bUnblocking;

 }OLESTDMESSAGEFILTER, FAR* LPOLESTDMESSAGEFILTER;

/* interface IMessageFilter implementation */
STDMETHODIMP OleStdMsgFilter_QueryInterface(
		LPMESSAGEFILTER lpThis, REFIID riid, LPVOID FAR* ppvObj);
STDMETHODIMP_(ULONG) OleStdMsgFilter_AddRef(LPMESSAGEFILTER lpThis);
STDMETHODIMP_(ULONG) OleStdMsgFilter_Release(LPMESSAGEFILTER lpThis);
STDMETHODIMP_(DWORD) OleStdMsgFilter_HandleInComingCall (
		LPMESSAGEFILTER     lpThis,
		DWORD               dwCallType,
		HTASK               htaskCaller,
		DWORD               dwTickCount,
		DWORD               dwReserved
);
STDMETHODIMP_(DWORD) OleStdMsgFilter_RetryRejectedCall (
		LPMESSAGEFILTER     lpThis,
		HTASK               htaskCallee,
		DWORD               dwTickCount,
		DWORD               dwRejectType
);
STDMETHODIMP_(DWORD) OleStdMsgFilter_MessagePending (
		LPMESSAGEFILTER     lpThis,
		HTASK               htaskCallee,
		DWORD               dwTickCount,
		DWORD               dwPendingType
);


static IMessageFilterVtbl g_OleStdMessageFilterVtbl = {
	OleStdMsgFilter_QueryInterface,
	OleStdMsgFilter_AddRef,
	OleStdMsgFilter_Release,
	OleStdMsgFilter_HandleInComingCall,
	OleStdMsgFilter_RetryRejectedCall,
	OleStdMsgFilter_MessagePending
};


/* GetTopWindowInWindowsTask
** -------------------------
**    Get the top most window that has focus in the given task to be
**    used as the parent for the busy dialog. we do this to handle the
**    case where a dialog window is currently up when we need to give
**    the busy dialog. if we use the current assigned parent window
**    (which typically will be the frame window of the app), then the
**    busy dialog will not be modal to the current active dialog
**    window.
*/
static HWND GetTopWindowInWindowsTask(HWND hwnd)
{
	HWND hwndActive = GetActiveWindow();
	if (!hwndActive)
		return hwnd;

	if (GetWindowTask(hwnd) == GetWindowTask(hwndActive))
		return hwndActive;
	else
		return hwnd;
}

STDAPI_(LPMESSAGEFILTER) OleStdMsgFilter_Create(
		HWND            hWndParent,
		LPSTR           szAppName,
		MSGPENDINGPROC  lpfnCallback,
		LPFNOLEUIHOOK   lpfnOleUIHook         // Busy dialog hook callback
)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter;
	LPMALLOC lpMalloc;

	if (CoGetMalloc(MEMCTX_TASK, (LPMALLOC FAR*)&lpMalloc) != NOERROR)
		return NULL;

	lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpMalloc->lpVtbl->Alloc(
			lpMalloc, (sizeof(OLESTDMESSAGEFILTER)));
	lpMalloc->lpVtbl->Release(lpMalloc);
	if (! lpStdMsgFilter) return NULL;

	lpStdMsgFilter->m_lpVtbl = &g_OleStdMessageFilterVtbl;
	lpStdMsgFilter->m_cRef = 1;
	lpStdMsgFilter->m_hWndParent = hWndParent;
	lpStdMsgFilter->m_dwInComingCallStatus = SERVERCALL_ISHANDLED;
	lpStdMsgFilter->m_lpfnHandleInComingCallback = NULL;
	lpStdMsgFilter->m_fEnableBusyDialog = TRUE;
	lpStdMsgFilter->m_fEnableNotRespondingDialog = TRUE;
	lpStdMsgFilter->m_lpszAppName = szAppName;
	lpStdMsgFilter->m_lpfnMessagePendingCallback = lpfnCallback;
	lpStdMsgFilter->m_lpfnBusyDialogHookCallback = lpfnOleUIHook;
	lpStdMsgFilter->m_hWndBusyDialog = NULL;
	lpStdMsgFilter->m_bUnblocking = FALSE;

  g_dwObjectCount ++;

	return (LPMESSAGEFILTER)lpStdMsgFilter;
}


/* OleStdMsgFilter_SetInComingStatus
** ---------------------------------
**    This is a private function that allows the caller to control what
**    value is returned from the IMessageFilter::HandleInComing method.
**
**    if a HandleInComingCallbackProc is installed by a call to
**    OleStdMsgFilter_SetHandleInComingCallbackProc, then this
**    overrides the dwIncomingCallStatus established by a call to
**    OleStdMsgFilter_SetInComingStatus.  Using
**    OleStdMsgFilter_SetInComingStatus allows the app to reject or
**    accept ALL in coming calls. Using a HandleInComingCallbackProc
**    allows the app to selectively handle or reject particular method
**    calls.
*/

STDAPI_(void) OleStdMsgFilter_SetInComingCallStatus(
		LPMESSAGEFILTER lpThis, DWORD dwInComingCallStatus)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;

	if (!IsBadWritePtr((LPVOID)lpStdMsgFilter,  sizeof(OLESTDMESSAGEFILTER)))
		lpStdMsgFilter->m_dwInComingCallStatus = dwInComingCallStatus;
	else
		OleDbgAssert(
			"OleStdMsgFilter_SetIncomingCallStatus: Invalid IMessageFilter*");

#if defined( _DEBUG )
	{
	char szBuf[80];
	char *szReturn;

	switch(dwInComingCallStatus) {
		case SERVERCALL_ISHANDLED:
			szReturn = "SERVERCALL_ISHANDLED";
			break;
		case SERVERCALL_REJECTED:
			szReturn = "SERVERCALL_REJECTED";
			break;
		case SERVERCALL_RETRYLATER:
			szReturn = "SERVERCALL_RETRYLATER";
			break;
		default:
			szReturn = "** ERROR: UNKNOWN **";
			break;
		}
	wsprintf(
		szBuf,
		"OleStdMsgFilter_SetInComingCallStatus: Status set to %s.\r\n",
		(LPSTR)szReturn
	);
	OleDbgOut3(szBuf);
	}
#endif

}


/* OleStdMsgFilter_SetHandleInComingCallbackProc
** ---------------------------------------------
**    This is a private function that allows the caller to install (or
**    de-install) a special callback function to selectively
**    handle/reject specific incoming method calls on particular
**    interfaces.
**
**    if a HandleInComingCallbackProc is installed by a call to
**    OleStdMsgFilter_SetHandleInComingCallbackProc, then this
**    overrides the dwIncomingCallStatus established by a call to
**    OleStdMsgFilter_SetInComingStatus.  Using
**    OleStdMsgFilter_SetInComingStatus allows the app to reject or
**    accept ALL in coming calls. Using a HandleInComingCallbackProc
**    allows the app to selectively handle or reject particular method
**    calls.
**
**    to de-install the HandleInComingCallbackProc, call
**          OleStdMsgFilter_SetHandleInComingCallbackProc(NULL);
**
**    Returns previous callback proc in effect.
*/

STDAPI_(HANDLEINCOMINGCALLBACKPROC)
	OleStdMsgFilter_SetHandleInComingCallbackProc(
		LPMESSAGEFILTER             lpThis,
		HANDLEINCOMINGCALLBACKPROC  lpfnHandleInComingCallback)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	HANDLEINCOMINGCALLBACKPROC    lpfnPrevCallback =
			lpStdMsgFilter->m_lpfnHandleInComingCallback;

	if (!IsBadWritePtr((LPVOID)lpStdMsgFilter, sizeof(OLESTDMESSAGEFILTER))) {
		lpStdMsgFilter->m_lpfnHandleInComingCallback =
				lpfnHandleInComingCallback;
	} else {
		OleDbgAssert(
			"OleStdMsgFilter_SetIncomingCallStatus: Invalid IMessageFilter*");
	}

#if defined( _DEBUG )
	{
		if (lpfnHandleInComingCallback)
			OleDbgOut3(
				"OleStdMsgFilter_SetHandleInComingCallbackProc SET\r\n");
		else
			OleDbgOut3(
				"OleStdMsgFilter_SetHandleInComingCallbackProc CLEARED\r\n");

	}
#endif  // _DEBUG

	return lpfnPrevCallback;
}


/* OleStdMsgFilter_GetInComingStatus
** ---------------------------------
**    This is a private function that returns the current
**    incoming call status.  Can be used to disable/enable options
**    in the calling application.
**
** Returns: one of
**
**    SERVERCALL_ISHANDLED
**    SERVERCALL_REJECTED
**    SERVERCALL_RETRYLATER
**    or -1 for ERROR
**
*/

STDAPI_(DWORD) OleStdMsgFilter_GetInComingCallStatus(
		LPMESSAGEFILTER lpThis)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	DWORD dwReturn;

	if (!IsBadReadPtr((LPVOID)lpStdMsgFilter,  sizeof(OLESTDMESSAGEFILTER)))
		dwReturn = lpStdMsgFilter->m_dwInComingCallStatus;
	else
		{
		OleDbgAssert(
			"OleStdMsgFilter_GetIncomingCallStatus: Invalid IMessageFilter*");
		dwReturn = (DWORD)-1;
		}

#if defined( _DEBUG )
	{
	char szBuf[80];
	char *szReturn;

	switch(dwReturn) {
		case SERVERCALL_ISHANDLED:
			szReturn = "SERVERCALL_ISHANDLED";
			break;
		case SERVERCALL_REJECTED:
			szReturn = "SERVERCALL_REJECTED";
			break;
		case SERVERCALL_RETRYLATER:
			szReturn = "SERVERCALL_RETRYLATER";
			break;
		default:
			szReturn = "-1";
			break;
		}
	wsprintf(
		szBuf,
		"OleStdMsgFilter_GetInComingCallStatus returns %s.\r\n",
		(LPSTR)szReturn
	);
	OleDbgOut3(szBuf);
	}
#endif

	return dwReturn;
}


/* OleStdMsgFilter_EnableBusyDialog
** --------------------------------
**    This function allows the caller to control whether
**    the busy dialog is enabled. this is the dialog put up when
**    IMessageFilter::RetryRejectedCall is called because the server
**    responded SERVERCALL_RETRYLATER or SERVERCALL_REJECTED.
**
**    if the busy dialog is NOT enabled, then the rejected call is
**    immediately canceled WITHOUT prompting the user. in this situation
**    OleStdMsgFilter_RetryRejectedCall always retuns
**    OLESTDCANCELRETRY canceling the outgoing LRPC call.
**    If the busy dialog is enabled, then the user is given the choice
**    of whether to retry, switch to, or cancel.
**
**    Returns previous dialog enable state
*/

STDAPI_(BOOL) OleStdMsgFilter_EnableBusyDialog(
		LPMESSAGEFILTER lpThis, BOOL fEnable)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	BOOL fPrevEnable = lpStdMsgFilter->m_fEnableBusyDialog;

	if (!IsBadWritePtr((LPVOID)lpStdMsgFilter,  sizeof(OLESTDMESSAGEFILTER)))
		lpStdMsgFilter->m_fEnableBusyDialog = fEnable;
	else
		OleDbgAssert(
				"OleStdMsgFilter_EnableBusyDialog: Invalid IMessageFilter*");

#if defined( _DEBUG )
	{
	char szBuf[80];
	wsprintf(
		szBuf,
		"OleStdMsgFilter_EnableBusyDialog: Dialog is %s.\r\n",
		fEnable ? (LPSTR)"ENABLED" : (LPSTR)"DISABLED"
	);
	OleDbgOut3(szBuf);
	}
#endif

	return fPrevEnable;
}


/* OleStdMsgFilter_EnableNotRespondingDialog
** -----------------------------------------
**    This function allows the caller to control whether
**    the app "NotResponding" (Blocked) dialog is enabled. this is the
**    dialog put up when IMessageFilter::MessagePending is called.
**    If the NotResponding dialog is enabled, then the user is given
**    the choice of whether to retry or switch to, but NOT to cancel.
**
**    Returns previous dialog enable state
*/

STDAPI_(BOOL) OleStdMsgFilter_EnableNotRespondingDialog(
		LPMESSAGEFILTER lpThis, BOOL fEnable)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	BOOL fPrevEnable = lpStdMsgFilter->m_fEnableNotRespondingDialog;

	if (!IsBadWritePtr((LPVOID)lpStdMsgFilter,  sizeof(OLESTDMESSAGEFILTER)))
		lpStdMsgFilter->m_fEnableNotRespondingDialog = fEnable;
	else
		OleDbgAssert(
				"OleStdMsgFilter_EnableNotRespondingDialog: Invalid IMessageFilter*");

#if defined( _DEBUG )
	{
	char szBuf[80];
	wsprintf(
		szBuf,
		"OleStdMsgFilter_EnableNotRespondingDialog: Dialog is %s.\r\n",
		fEnable ? (LPSTR)"ENABLED" : (LPSTR)"DISABLED"
	);
	OleDbgOut3(szBuf);
	}
#endif

	return fPrevEnable;
}


/* OleStdMsgFilter_SetParentWindow
** -------------------------------
**    This function allows caller to set which window will be used as
**    the parent for the busy dialog.
**
**    OLE2NOTE: it would be inportant for an in-place active server to
**    reset this to its current in-place frame window when in-place
**    activated. if the hWndParent is set to NULL then the dialogs will
**    be parented to the desktop.
**
**    Returns: previous parent window
*/

STDAPI_(HWND) OleStdMsgFilter_SetParentWindow(
		LPMESSAGEFILTER lpThis, HWND hWndParent)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	HWND hWndPrev = lpStdMsgFilter->m_hWndParent;

	lpStdMsgFilter->m_hWndParent = hWndParent;
	return hWndPrev;
}


STDMETHODIMP OleStdMsgFilter_QueryInterface(
		LPMESSAGEFILTER lpThis, REFIID riid, LPVOID FAR* ppvObj)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	SCODE scode;

	/* Two interfaces supported: IUnknown, IMessageFilter
	*/

	if (IsEqualIID(riid, &IID_IMessageFilter) || IsEqualIID(riid, &IID_IUnknown)) {
		lpStdMsgFilter->m_cRef++;   // A pointer to this object is returned
		*ppvObj = lpThis;
		scode = S_OK;
	}
	else {                 // unsupported interface
		*ppvObj = NULL;
		scode = E_NOINTERFACE;
	}

	return ResultFromScode(scode);
}


STDMETHODIMP_(ULONG) OleStdMsgFilter_AddRef(LPMESSAGEFILTER lpThis)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	return ++lpStdMsgFilter->m_cRef;
}

STDMETHODIMP_(ULONG) OleStdMsgFilter_Release(LPMESSAGEFILTER lpThis)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	LPMALLOC lpMalloc;

	if (--lpStdMsgFilter->m_cRef != 0) // Still used by others
		return lpStdMsgFilter->m_cRef;

	// Free storage
	if (CoGetMalloc(MEMCTX_TASK, (LPMALLOC FAR*)&lpMalloc) != NOERROR)
		return (ULONG)0;

	lpMalloc->lpVtbl->Free(lpMalloc, lpStdMsgFilter);
	lpMalloc->lpVtbl->Release(lpMalloc);

  g_dwObjectCount --;

	return (ULONG)0;
}


STDMETHODIMP_(DWORD) OleStdMsgFilter_HandleInComingCall (
		LPMESSAGEFILTER     lpThis,
		DWORD               dwCallType,
		HTASK               htaskCaller,
		DWORD               dwTickCount,
		DWORD               dwReserved
)
{
	LPOLESTDMESSAGEFILTER lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;

	/* if a HandleInComingCallbackProc is in effect, then this
	**    overrides dwIncomingCallStatus established by a call to
	**    OleStdMsgFilter_SetInComingStatus.  we will call this
	**    callback to allow the app to selectively handle or reject
	**    incoming method calls. the LPINTERFACEINFO parameter
	**    describes which method is being called.
	*/
	if (lpStdMsgFilter->m_lpfnHandleInComingCallback &&
		!IsBadCodePtr((FARPROC)lpStdMsgFilter->m_lpfnHandleInComingCallback)){
		return lpStdMsgFilter->m_lpfnHandleInComingCallback(
				dwCallType,
				htaskCaller,
				dwTickCount,
				(LPINTERFACEINFO)dwReserved
		);
	}

	switch (dwCallType) {
		case CALLTYPE_TOPLEVEL:
			/* OLE2NOTE: we currently have NO pending outgoing call and
			**    there is a new toplevel incoming call.
			**    this call may be rejected.
			*/
			return lpStdMsgFilter->m_dwInComingCallStatus;

		case CALLTYPE_TOPLEVEL_CALLPENDING:
			/* OLE2NOTE: we currently HAVE a pending outgoing call and
			**    there is a new toplevel incoming call.
			**    this call may be rejected.
			*/
			return lpStdMsgFilter->m_dwInComingCallStatus;

		case CALLTYPE_NESTED:
			/* OLE2NOTE: we currently HAVE a pending outgoing call and
			**    there callback on behalf of the previous outgoing
			**    call. this type of call should ALWAYS be handled.
			*/
			return SERVERCALL_ISHANDLED;

		case CALLTYPE_ASYNC:
			/* OLE2NOTE: we currently have NO pending outgoing call and
			**    there is a new asyncronis incoming call.
			**    this call can NEVER be rejected. OLE actually ignores
			**    the return code in this case and always allows the
			**    call through.
			*/
			return SERVERCALL_ISHANDLED;    // value returned does not matter

		case CALLTYPE_ASYNC_CALLPENDING:
			/* OLE2NOTE: we currently HAVE a pending outgoing call and
			**    there is a new asyncronis incoming call.
			**    this call can NEVER be rejected. OLE ignore the
			**    return code in this case.
			*/
			return SERVERCALL_ISHANDLED;    // value returned does not

		default:
			OleDbgAssert(
					"OleStdMsgFilter_HandleInComingCall: Invalid CALLTYPE");
			return lpStdMsgFilter->m_dwInComingCallStatus;
	}
}

STDMETHODIMP_(DWORD) OleStdMsgFilter_RetryRejectedCall (
		LPMESSAGEFILTER     lpThis,
		HTASK               htaskCallee,
		DWORD               dwTickCount,
		DWORD               dwRejectType
)
{
	LPOLESTDMESSAGEFILTER   lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	DWORD                   dwRet = 0;
	UINT                    uRet;
#if defined( _DEBUG )
	char                    szBuf[80];
#endif
	OLEDBG_BEGIN2("OleStdMsgFilter_RetryRejectedCall\r\n")

	/* OLE2NOTE: we should only put up the application busy dialog when
	**    the callee has responded SERVERCALL_RETRYLATER. if the
	**    dwRejectType is SERVERCALL_REJECTED then there is something
	**    seriously wrong with the callee (perhaps a severe low memory
	**    situation). we don't want to even try to "Switch To" this app
	**    or even try to "Retry".
	*/
	if (dwRejectType == SERVERCALL_RETRYLATER &&
			lpStdMsgFilter->m_fEnableBusyDialog) {

		OLEUIBUSY bz;

		/* OLE2NOTE: we do not want to put up the Busy dialog immediately
		**    the when an app says RETRYLATER. we should continue retrying
		**    for a while in case the app can un-busy itself in a
		**    reasonable amount of time.
		*/
		if (dwTickCount <= (DWORD)OLESTDRETRYDELAY) {
			dwRet = 500;                  // Retry after .5 sec
			OLEDBG_END2
			return dwRet;
		}

		/*
		** Set up structure for calling OLEUIBUSY dialog
		*/

		bz.cbStruct = sizeof(OLEUIBUSY);
		bz.dwFlags = 0L;
		bz.hWndOwner =GetTopWindowInWindowsTask(lpStdMsgFilter->m_hWndParent);
		bz.lpszCaption = lpStdMsgFilter->m_lpszAppName;
		bz.lpfnHook = lpStdMsgFilter->m_lpfnBusyDialogHookCallback;
		bz.lCustData = 0;
		bz.hInstance = NULL;
		bz.lpszTemplate = NULL;
		bz.hResource = 0;
		bz.hTask = htaskCallee;
		bz.lphWndDialog = NULL; // We don't need the hDlg for this call

		uRet = OleUIBusy(&bz);

		switch (uRet) {
			case OLEUI_BZ_RETRYSELECTED:
				dwRet = 0;                  // Retry immediately
				break;

			case OLEUI_CANCEL:
				dwRet = OLESTDCANCELRETRY;  // Cancel pending outgoing call
				break;

			case OLEUI_BZERR_HTASKINVALID:
				// Htask was invalid, return OLESTDRETRYDELAY anyway
				dwRet = OLESTDRETRYDELAY;   // Retry after <retry delay> msec

#if defined( _DEBUG )
				wsprintf(
						szBuf,
						"OleStdMsgFilter_RetryRejectedCall, HTASK 0x%x invalid\r\n",
						htaskCallee
				);
				OleDbgOut3(szBuf);
#endif
				break;
		}
	} else {
		dwRet = OLESTDCANCELRETRY;  // Cancel pending outgoing call
	}

#if defined( _DEBUG )
	wsprintf(szBuf,
			 "OleStdMsgFilter_RetryRejectedCall returns %d\r\n",
			 dwRet);
	OleDbgOut3(szBuf);
#endif

	OLEDBG_END2
	return dwRet;
}



/* a significant message is consider a mouse click or keyboard input. */
#define IS_SIGNIFICANT_MSG(lpmsg)   \
	(   \
		(PeekMessage((lpmsg), NULL, WM_LBUTTONDOWN, WM_LBUTTONDOWN, \
				 PM_NOREMOVE | PM_NOYIELD)) \
	 || (PeekMessage((lpmsg), NULL, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK, \
				 PM_NOREMOVE | PM_NOYIELD)) \
	 || (PeekMessage((lpmsg), NULL, WM_NCLBUTTONDOWN, WM_NCLBUTTONDOWN, \
				 PM_NOREMOVE | PM_NOYIELD)) \
	 || (PeekMessage((lpmsg), NULL, WM_NCLBUTTONDBLCLK, WM_NCLBUTTONDBLCLK, \
				 PM_NOREMOVE | PM_NOYIELD)) \
	 || (PeekMessage((lpmsg), NULL, WM_KEYDOWN, WM_KEYDOWN, \
				 PM_NOREMOVE | PM_NOYIELD)) \
	 || (PeekMessage((lpmsg), NULL, WM_SYSKEYDOWN, WM_SYSKEYDOWN, \
				 PM_NOREMOVE | PM_NOYIELD)) \
	)

STDMETHODIMP_(DWORD) OleStdMsgFilter_MessagePending (
		LPMESSAGEFILTER     lpThis,
		HTASK               htaskCallee,
		DWORD               dwTickCount,
		DWORD               dwPendingType
)
{
	LPOLESTDMESSAGEFILTER   lpStdMsgFilter = (LPOLESTDMESSAGEFILTER)lpThis;
	DWORD                   dwReturn = PENDINGMSG_WAITDEFPROCESS;
	MSG                     msg;
	BOOL                    fIsSignificantMsg = IS_SIGNIFICANT_MSG(&msg);
	UINT                    uRet;

#if defined( _DEBUG )
	char szBuf[128];
	wsprintf(
			szBuf,
			"OleStdMsgFilter_MessagePending, dwTickCount = 0x%lX\r\n",
			(DWORD)dwTickCount
	);
	OleDbgOut4(szBuf);
#endif

	/* OLE2NOTE: If our tick count for this call exceeds our standard retry
	**      delay, then we need to put up the dialog.  We will only
	**      consider putting up the dialog if the user has issued a
	**      "significant" event (ie. mouse click or keyboard event). a
	**      simple mouse move should NOT trigger this dialog.
	**      Since our call to
	**      OleUIBusy below enters a DialogBox() message loop, there's a
	**      possibility that another call will be initiated during the dialog,
	**      and this procedure will be re-entered.  Just so we don't put up
	**      two dialogs at a time, we use the m_bUnblocking varable
	**      to keep track of this situation.
	*/

	if (dwTickCount > (DWORD)OLESTDRETRYDELAY && fIsSignificantMsg
			&& !lpStdMsgFilter->m_bUnblocking)
	{

		if (lpStdMsgFilter->m_fEnableNotRespondingDialog)
		{
		OLEUIBUSY bz;

		lpStdMsgFilter->m_bUnblocking = TRUE;

		// Eat messages in our queue that we do NOT want to be dispatched
		while (PeekMessage(&msg, NULL, WM_CLOSE, WM_CLOSE, PM_REMOVE | PM_NOYIELD));

		/* Set up structure for calling OLEUIBUSY dialog,
		** using the "not responding" variety
		*/

		bz.cbStruct = sizeof(OLEUIBUSY);
		bz.dwFlags = BZ_NOTRESPONDINGDIALOG;
		bz.hWndOwner =GetTopWindowInWindowsTask(lpStdMsgFilter->m_hWndParent);
		bz.lpszCaption = lpStdMsgFilter->m_lpszAppName;
		bz.lpfnHook = lpStdMsgFilter->m_lpfnBusyDialogHookCallback;
		bz.lCustData = 0;
		bz.hInstance = NULL;
		bz.lpszTemplate = NULL;
		bz.hResource = 0;
		bz.hTask = htaskCallee;

		/* Set up the address to the hWnd in our MsgFilter structure.  The
		** call to OleUIBusy will fill this in with the hWnd of the busy
		** dialog box
		*/

		bz.lphWndDialog =  (HWND FAR *)&(lpStdMsgFilter->m_hWndBusyDialog);
		uRet = OleUIBusy(&bz);

		lpStdMsgFilter->m_bUnblocking = FALSE;

		return PENDINGMSG_WAITNOPROCESS;
		}
#if defined( _DEBUG )
		else {
			OleDbgOut3("OleStdMsgFilter_MessagePending: BLOCKED but dialog Disabled\r\n");
		}
#endif
	}

	/* If we're already unblocking, we're being re-entered.  Don't
	** process message
	*/

	if (lpStdMsgFilter->m_bUnblocking)
		return PENDINGMSG_WAITDEFPROCESS;

	/* OLE2NOTE: If we have a callback function set up, call it with the
	** current message.  If not, tell OLE LPRC mechanism to automatically
	** handle all messages.
	*/
	if (lpStdMsgFilter->m_lpfnMessagePendingCallback &&
		!IsBadCodePtr((FARPROC)lpStdMsgFilter->m_lpfnMessagePendingCallback)){
		MSG msg;

		/* OLE2NOTE: the app provided a MessagePendingCallback
		**    function. we will PeekMessage for the first message in
		**    the queue and pass it to the app. the app in its callback
		**    function can decide to Dispatch this message or it can
		**    PeekMessage on its own giving particular message filter
		**    criteria. if the app returns TRUE then we return
		**    PENDINGMSG_WAITNOPROCESS to OLE telling OLE to leave the
		**    message in the queue. If the app returns FALSE, then we
		**    return PENDINGMSG_WAITDEFPROCESS to OLE telling OLE to do
		**    its default processing with the message. by default OLE
		**    dispatches system messages and eats other messages and
		**    beeps.
		*/
		if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD)) {

			if (lpStdMsgFilter->m_lpfnMessagePendingCallback(&msg)) {
				/* TRUE return means that the app processed message.
				**
				** NOTE: (CHANGE FROM OLE2.0 VERSION) we leave it up to
				**    the callback routine to remove the message if it
				**    wants.
				*/
				dwReturn = PENDINGMSG_WAITNOPROCESS;
			} else {
				/* FALSE means that the app did not process the
				**    message. we will let OLE take its
				**    default action.
				**
				** NOTE: (CHANGE FROM OLE2.0 VERSION) we used to return
				**    PENDINGMSG_WAITNOPROCESS to leave the message in
				**    the queue; now we return PENDINGMSG_WAITDEFPROCESS
				**    to let OLE do default processing.
				*/
				dwReturn = PENDINGMSG_WAITDEFPROCESS;

#if defined( _DEBUG )
				wsprintf(
						szBuf,
						"Message (0x%x) (wParam=0x%x, lParam=0x%lx) using WAITDEFPROCESS while blocked\r\n",
						msg.message,
						msg.lParam,
						msg.wParam
				);
				OleDbgOut2(szBuf);
#endif  // _DEBUG
			}
		}
	}

	return dwReturn;
}
