/*
 *    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
   LPOLESTR                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,
      LPINTERFACEINFO     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,
      LPOLESTR           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,
      LPINTERFACEINFO     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,
            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;
}
