/*
 *    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
    LPTSTR                   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,
#ifdef WIN32
        LPINTERFACEINFO     dwReserved
#else
        DWORD               dwReserved
#endif
);
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,
        LPTSTR           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;

    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(
            TEXT("OleStdMsgFilter_SetIncomingCallStatus: Invalid IMessageFilter*"));

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

    switch(dwInComingCallStatus) {
        case SERVERCALL_ISHANDLED:
            szReturn = TEXT("SERVERCALL_ISHANDLED");
            break;
        case SERVERCALL_REJECTED:
            szReturn = TEXT("SERVERCALL_REJECTED");
            break;
        case SERVERCALL_RETRYLATER:
            szReturn = TEXT("SERVERCALL_RETRYLATER");
            break;
        default:
            szReturn = TEXT("** ERROR: UNKNOWN **");
            break;
        }
    wsprintf(
        szBuf,
        TEXT("OleStdMsgFilter_SetInComingCallStatus: Status set to %s.\r\n"),
        (LPTSTR)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(
            TEXT("OleStdMsgFilter_SetIncomingCallStatus: Invalid IMessageFilter*"));
    }

#if defined( _DEBUG )
    {
        if (lpfnHandleInComingCallback)
            OleDbgOut3(
                TEXT("OleStdMsgFilter_SetHandleInComingCallbackProc SET\r\n"));
        else
            OleDbgOut3(
                TEXT("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(
            TEXT("OleStdMsgFilter_GetIncomingCallStatus: Invalid IMessageFilter*"));
        dwReturn = (DWORD)-1;
        }

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

    switch(dwReturn) {
        case SERVERCALL_ISHANDLED:
            szReturn = TEXT("SERVERCALL_ISHANDLED");
            break;
        case SERVERCALL_REJECTED:
            szReturn = TEXT("SERVERCALL_REJECTED");
            break;
        case SERVERCALL_RETRYLATER:
            szReturn = TEXT("SERVERCALL_RETRYLATER");
            break;
        default:
            szReturn = TEXT("-1");
            break;
        }
    wsprintf(
        szBuf,
        TEXT("OleStdMsgFilter_GetInComingCallStatus returns %s.\r\n"),
        (LPTSTR)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(
           TEXT("OleStdMsgFilter_EnableBusyDialog: Invalid IMessageFilter*"));

#if defined( _DEBUG )
    {
    TCHAR szBuf[80];
    wsprintf(
        szBuf,
        TEXT("OleStdMsgFilter_EnableBusyDialog: Dialog is %s.\r\n"),
        fEnable ? (LPTSTR) TEXT("ENABLED") : (LPTSTR) TEXT("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(
           TEXT("OleStdMsgFilter_EnableNotRespondingDialog: Invalid IMessageFilter*"));

#if defined( _DEBUG )
    {
    TCHAR szBuf[80];
    wsprintf(
        szBuf,
        TEXT("OleStdMsgFilter_EnableNotRespondingDialog: Dialog is %s.\r\n"),
        fEnable ? (LPTSTR) TEXT("ENABLED") : (LPTSTR) TEXT("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);
    return (ULONG)0;
}


STDMETHODIMP_(DWORD) OleStdMsgFilter_HandleInComingCall (
        LPMESSAGEFILTER     lpThis,
        DWORD               dwCallType,
        HTASK               htaskCaller,
        DWORD               dwTickCount,
#ifdef WIN32
        LPINTERFACEINFO     dwReserved
#else
        DWORD               dwReserved
#endif
)
{
    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(
                TEXT("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 )
    TCHAR                   szBuf[80];
#endif
    OLEDBG_BEGIN2(TEXT("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,
                        TEXT("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,
             TEXT("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 )
    TCHAR szBuf[128];
    wsprintf(
            szBuf,
            TEXT("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(TEXT("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,
                        TEXT("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;
}