mirror of https://github.com/lianthony/NT4.0
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.
378 lines
8.9 KiB
378 lines
8.9 KiB
// This is a part of the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) 1992-1995 Microsoft Corporation
|
|
// All rights reserved.
|
|
//
|
|
// This source code is only intended as a supplement to the
|
|
// Microsoft Foundation Classes Reference and related
|
|
// electronic documentation provided with the library.
|
|
// See these sources for detailed information regarding the
|
|
// Microsoft Foundation Classes product.
|
|
|
|
#include "stdafx.h"
|
|
|
|
#ifdef AFX_OLE4_SEG
|
|
#pragma code_seg(AFX_OLE4_SEG)
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define new DEBUG_NEW
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleMessageFilter::IMessageFilter implementation
|
|
|
|
#ifdef AFX_INIT_SEG
|
|
#pragma code_seg(AFX_INIT_SEG)
|
|
#endif
|
|
|
|
COleMessageFilter::COleMessageFilter()
|
|
{
|
|
// begin in not-busy state
|
|
m_nBusyCount = 0;
|
|
|
|
// dialogs are enabled by default
|
|
m_bEnableBusy = TRUE;
|
|
m_bEnableNotResponding = TRUE;
|
|
|
|
m_nBusyReply = SERVERCALL_RETRYLATER;
|
|
// effective only when m_nBusyCount != 0
|
|
|
|
m_nRetryReply = 10000; // default is 10 sec
|
|
m_nTimeout = 8000; // default is 8 sec
|
|
|
|
m_bUnblocking = FALSE;
|
|
// TRUE to avoid re-entrancy when busy dialog is up
|
|
|
|
m_bRegistered = FALSE;
|
|
|
|
ASSERT_VALID(this);
|
|
}
|
|
|
|
#ifdef AFX_TERM_SEG
|
|
#pragma code_seg(AFX_TERM_SEG)
|
|
#endif
|
|
|
|
COleMessageFilter::~COleMessageFilter()
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
Revoke();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Busy state management
|
|
|
|
#ifdef AFX_OLE4_SEG
|
|
#pragma code_seg(AFX_OLE4_SEG)
|
|
#endif
|
|
|
|
void COleMessageFilter::BeginBusyState()
|
|
{
|
|
ASSERT_VALID(this);
|
|
++m_nBusyCount;
|
|
}
|
|
|
|
void COleMessageFilter::EndBusyState()
|
|
{
|
|
ASSERT_VALID(this);
|
|
if (m_nBusyCount != 0)
|
|
--m_nBusyCount;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleMessageFilter operations
|
|
|
|
#ifdef AFX_INIT_SEG
|
|
#pragma code_seg(AFX_INIT_SEG)
|
|
#endif
|
|
|
|
BOOL COleMessageFilter::Register()
|
|
{
|
|
ASSERT_VALID(this);
|
|
ASSERT(!m_bRegistered); // calling Register twice?
|
|
|
|
if (::CoRegisterMessageFilter(&m_xMessageFilter, NULL) == S_OK)
|
|
{
|
|
m_bRegistered = TRUE;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef AFX_TERM_SEG
|
|
#pragma code_seg(AFX_TERM_SEG)
|
|
#endif
|
|
|
|
void COleMessageFilter::Revoke()
|
|
{
|
|
ASSERT_VALID(this);
|
|
|
|
if (m_bRegistered)
|
|
{
|
|
::CoRegisterMessageFilter(NULL, NULL);
|
|
m_bRegistered = FALSE;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleMessageFilter standard implementation of callbacks
|
|
|
|
#ifdef AFX_OLE4_SEG
|
|
#pragma code_seg(AFX_OLE4_SEG)
|
|
#endif
|
|
|
|
BOOL COleMessageFilter::OnMessagePending(const MSG* pMsg)
|
|
{
|
|
// By default we rely on OLE's default message handling for every message
|
|
// except WM_PAINT messages. WM_PAINT messages should not generate
|
|
// out-going calls.
|
|
|
|
BOOL bEatMessage = FALSE;
|
|
|
|
switch (pMsg->message)
|
|
{
|
|
case WM_PAINT:
|
|
DispatchMessage(pMsg);
|
|
bEatMessage = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (bEatMessage)
|
|
{
|
|
MSG msg;
|
|
::PeekMessage(&msg, NULL, pMsg->message, pMsg->message,
|
|
PM_REMOVE|PM_NOYIELD);
|
|
}
|
|
return bEatMessage;
|
|
}
|
|
|
|
BOOL COleMessageFilter::IsSignificantMessage(MSG*)
|
|
{
|
|
// check for "significant" messages in the queue
|
|
UINT rgnMsgs[] =
|
|
{
|
|
WM_KEYDOWN, WM_SYSKEYDOWN,
|
|
WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN,
|
|
WM_NCLBUTTONDOWN, WM_NCRBUTTONDOWN, WM_NCMBUTTONDOWN,
|
|
WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK,
|
|
WM_NCLBUTTONDBLCLK, WM_NCRBUTTONDBLCLK, WM_NCMBUTTONDBLCLK
|
|
};
|
|
MSG msg;
|
|
for (int i = 0; i < _countof(rgnMsgs); i++)
|
|
{
|
|
if (::PeekMessage(&msg, NULL, rgnMsgs[i], rgnMsgs[i],
|
|
PM_NOREMOVE|PM_NOYIELD))
|
|
{
|
|
if ((msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) &&
|
|
(HIWORD(msg.lParam) & KF_REPEAT))
|
|
{
|
|
// a key-repeat is a non-significant message
|
|
continue;
|
|
}
|
|
|
|
// "significant" message is waiting in the queue
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// no significant messages in the queue
|
|
return FALSE;
|
|
}
|
|
|
|
int COleMessageFilter::OnBusyDialog(HTASK htaskBusy)
|
|
{
|
|
COleBusyDialog dlg(htaskBusy, FALSE);
|
|
|
|
int nResult = -1;
|
|
TRY
|
|
{
|
|
if (dlg.DoModal() == IDOK)
|
|
nResult = dlg.GetSelectionType();
|
|
}
|
|
END_TRY
|
|
|
|
return nResult;
|
|
}
|
|
|
|
int COleMessageFilter::OnNotRespondingDialog(HTASK htaskBusy)
|
|
{
|
|
COleBusyDialog dlg(htaskBusy, TRUE);
|
|
|
|
int nResult = -1;
|
|
TRY
|
|
{
|
|
if (dlg.DoModal() == IDOK)
|
|
nResult = dlg.GetSelectionType();
|
|
}
|
|
END_TRY
|
|
|
|
return nResult;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleMessageFilter OLE interface implementation
|
|
|
|
BEGIN_INTERFACE_MAP(COleMessageFilter, CCmdTarget)
|
|
INTERFACE_PART(COleMessageFilter, IID_IMessageFilter, MessageFilter)
|
|
END_INTERFACE_MAP()
|
|
|
|
STDMETHODIMP_(ULONG) COleMessageFilter::XMessageFilter::AddRef()
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleMessageFilter, MessageFilter)
|
|
return pThis->ExternalAddRef();
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) COleMessageFilter::XMessageFilter::Release()
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleMessageFilter, MessageFilter)
|
|
return pThis->ExternalRelease();
|
|
}
|
|
|
|
STDMETHODIMP COleMessageFilter::XMessageFilter::QueryInterface(
|
|
REFIID iid, LPVOID* ppvObj)
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleMessageFilter, MessageFilter)
|
|
return pThis->ExternalQueryInterface(&iid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(DWORD) COleMessageFilter::XMessageFilter::HandleInComingCall(
|
|
DWORD dwCallType, HTASK /*htaskCaller*/,
|
|
DWORD /*dwTickCount*/, LPINTERFACEINFO /*lpInterfaceInfo*/)
|
|
{
|
|
METHOD_PROLOGUE_EX_(COleMessageFilter, MessageFilter)
|
|
|
|
// check for application busy first...
|
|
if (pThis->m_nBusyCount == 0)
|
|
{
|
|
if (dwCallType == CALLTYPE_TOPLEVEL ||
|
|
dwCallType == CALLTYPE_TOPLEVEL_CALLPENDING)
|
|
{
|
|
// make sure CWinThread::OnIdle has a chance to run later
|
|
MSG msg;
|
|
if (!::PeekMessage(&msg, NULL, WM_KICKIDLE, WM_KICKIDLE, PM_NOREMOVE))
|
|
::PostThreadMessage(GetCurrentThreadId(), WM_KICKIDLE, 0, 0);
|
|
}
|
|
return SERVERCALL_ISHANDLED;
|
|
}
|
|
|
|
if (dwCallType == CALLTYPE_TOPLEVEL ||
|
|
dwCallType == CALLTYPE_TOPLEVEL_CALLPENDING)
|
|
{
|
|
// application is busy and we have rejectable CALLTYPE
|
|
return pThis->m_nBusyReply;
|
|
}
|
|
|
|
// application is busy, but CALLTYPE indicates that it *must* be handled
|
|
return SERVERCALL_ISHANDLED;
|
|
}
|
|
|
|
STDMETHODIMP_(DWORD) COleMessageFilter::XMessageFilter::RetryRejectedCall(
|
|
HTASK htaskCallee, DWORD dwTickCount, DWORD dwRejectType)
|
|
{
|
|
METHOD_PROLOGUE_EX(COleMessageFilter, MessageFilter)
|
|
ASSERT_VALID(pThis);
|
|
|
|
// rejected calls get cancelled regardless of timeout
|
|
if (dwRejectType == SERVERCALL_REJECTED)
|
|
return (DWORD)-1;
|
|
|
|
// if insignificant time has passed, don't panic -- just retry
|
|
if (dwTickCount <= pThis->m_nRetryReply)
|
|
return 0; // retry right away (0-100 are retry immediate)
|
|
|
|
// too much time has passed, do something more drastic
|
|
if (pThis->m_bEnableBusy)
|
|
{
|
|
// show busy dialog
|
|
int selType = pThis->OnBusyDialog(htaskCallee);
|
|
|
|
// take action depending on selection
|
|
switch (selType)
|
|
{
|
|
case -1:
|
|
return (DWORD)-1; // cancel outgoing call
|
|
|
|
case COleBusyDialog::retry:
|
|
return 0; // retry immediately
|
|
}
|
|
}
|
|
return pThis->m_nRetryReply; // use standard retry timeout
|
|
}
|
|
|
|
STDMETHODIMP_(DWORD) COleMessageFilter::XMessageFilter::MessagePending(
|
|
HTASK htaskCallee, DWORD dwTickCount, DWORD /*dwPendingType*/)
|
|
{
|
|
METHOD_PROLOGUE_EX(COleMessageFilter, MessageFilter)
|
|
ASSERT_VALID(pThis);
|
|
|
|
MSG msg;
|
|
if (dwTickCount > pThis->m_nTimeout && !pThis->m_bUnblocking &&
|
|
pThis->IsSignificantMessage(&msg))
|
|
{
|
|
if (pThis->m_bEnableNotResponding)
|
|
{
|
|
pThis->m_bUnblocking = TRUE; // avoid reentrant calls
|
|
|
|
// eat all mouse messages in our queue
|
|
while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,
|
|
PM_REMOVE|PM_NOYIELD))
|
|
;
|
|
// eat all keyboard messages in our queue
|
|
while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,
|
|
PM_REMOVE|PM_NOYIELD))
|
|
;
|
|
|
|
// show not responding dialog
|
|
pThis->OnNotRespondingDialog(htaskCallee);
|
|
pThis->m_bUnblocking = FALSE;
|
|
|
|
return PENDINGMSG_WAITNOPROCESS;
|
|
}
|
|
}
|
|
|
|
// don't process re-entrant messages
|
|
if (pThis->m_bUnblocking)
|
|
return PENDINGMSG_WAITDEFPROCESS;
|
|
|
|
// allow application to process pending message
|
|
if (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE|PM_NOYIELD) &&
|
|
pThis->OnMessagePending(&msg))
|
|
{
|
|
// app has processed the message (and removed it from the queue)
|
|
return PENDINGMSG_WAITNOPROCESS;
|
|
}
|
|
|
|
// by default we return pending MSG wait
|
|
return PENDINGMSG_WAITNOPROCESS;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// COleMessageFilter diagnostics
|
|
|
|
#ifdef _DEBUG
|
|
void COleMessageFilter::AssertValid() const
|
|
{
|
|
CCmdTarget::AssertValid();
|
|
}
|
|
|
|
void COleMessageFilter::Dump(CDumpContext& dc) const
|
|
{
|
|
CCmdTarget::Dump(dc);
|
|
|
|
dc << "m_bRegistered = " << m_bRegistered;
|
|
dc << "\nm_nBusyCount = " << m_nBusyCount;
|
|
dc << "\nm_bEnableBusy = " << m_bEnableBusy;
|
|
dc << "\nm_bEnableNotResponding = " << m_bEnableNotResponding;
|
|
dc << "\nm_bUnblocking = " << m_bUnblocking;
|
|
dc << "\nm_nRetryReply = " << m_nRetryReply;
|
|
dc << "\nm_nBusyReply = " << m_nBusyReply;
|
|
dc << "\nm_nTimeout = " << m_nTimeout;
|
|
|
|
dc << "\n";
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|