home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / olemsgf.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-16  |  9.1 KB  |  367 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_OLE4_SEG
  14. #pragma code_seg(AFX_OLE4_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // COleMessageFilter::IMessageFilter implementation
  26.  
  27. #ifdef AFX_INIT_SEG
  28. #pragma code_seg(AFX_INIT_SEG)
  29. #endif
  30.  
  31. COleMessageFilter::COleMessageFilter()
  32. {
  33.     // begin in not-busy state
  34.     m_nBusyCount = 0;
  35.  
  36.     // dialogs are enabled by default
  37.     m_bEnableBusy = TRUE;
  38.     m_bEnableNotResponding = TRUE;
  39.  
  40.     m_nBusyReply = SERVERCALL_RETRYLATER;
  41.         // effective only when m_nBusyCount != 0
  42.  
  43.     m_nRetryReply = 10000;  // default is 10 sec
  44.     m_nTimeout = 8000;  // default is 8 sec
  45.  
  46.     m_bUnblocking = FALSE;
  47.         // TRUE to avoid re-entrancy when busy dialog is up
  48.  
  49.     m_bRegistered = FALSE;
  50.  
  51.     ASSERT_VALID(this);
  52. }
  53.  
  54. #ifdef AFX_TERM_SEG
  55. #pragma code_seg(AFX_TERM_SEG)
  56. #endif
  57.  
  58. COleMessageFilter::~COleMessageFilter()
  59. {
  60.     ASSERT_VALID(this);
  61.  
  62.     Revoke();
  63. }
  64.  
  65. /////////////////////////////////////////////////////////////////////////////
  66. // Busy state management
  67.  
  68. #ifdef AFX_OLE4_SEG
  69. #pragma code_seg(AFX_OLE4_SEG)
  70. #endif
  71.  
  72. void COleMessageFilter::BeginBusyState()
  73. {
  74.     ASSERT_VALID(this);
  75.     ++m_nBusyCount;
  76. }
  77.  
  78. void COleMessageFilter::EndBusyState()
  79. {
  80.     ASSERT_VALID(this);
  81.     if (m_nBusyCount != 0)
  82.         --m_nBusyCount;
  83. }
  84.  
  85. /////////////////////////////////////////////////////////////////////////////
  86. // COleMessageFilter operations
  87.  
  88. #ifdef AFX_INIT_SEG
  89. #pragma code_seg(AFX_INIT_SEG)
  90. #endif
  91.  
  92. BOOL COleMessageFilter::Register()
  93. {
  94.     ASSERT_VALID(this);
  95.     ASSERT(!m_bRegistered); // calling Register twice?
  96.  
  97.     if (::CoRegisterMessageFilter(&m_xMessageFilter, NULL) == S_OK)
  98.     {
  99.         m_bRegistered = TRUE;
  100.         return TRUE;
  101.     }
  102.     return FALSE;
  103. }
  104.  
  105. #ifdef AFX_TERM_SEG
  106. #pragma code_seg(AFX_TERM_SEG)
  107. #endif
  108.  
  109. void COleMessageFilter::Revoke()
  110. {
  111.     ASSERT_VALID(this);
  112.  
  113.     if (m_bRegistered)
  114.     {
  115.         ::CoRegisterMessageFilter(NULL, NULL);
  116.         m_bRegistered = FALSE;
  117.     }
  118. }
  119.  
  120. /////////////////////////////////////////////////////////////////////////////
  121. // COleMessageFilter standard implementation of callbacks
  122.  
  123. #ifdef AFX_OLE4_SEG
  124. #pragma code_seg(AFX_OLE4_SEG)
  125. #endif
  126.  
  127. BOOL COleMessageFilter::OnMessagePending(const MSG* /*pMsg*/)
  128. {
  129.     // By default we rely on OLE's default message handling for every message
  130.     //  except WM_PAINT messages.  WM_PAINT messages should not generate
  131.     //  out-going calls.
  132.  
  133.     BOOL bEatMessage = FALSE;
  134.     MSG msg;
  135.     while (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE|PM_NOYIELD))
  136.     {
  137.         bEatMessage = TRUE;
  138.         DispatchMessage(&msg);
  139.     }
  140.     return bEatMessage;
  141. }
  142.  
  143. AFX_STATIC_DATA const UINT _afxSignificantMsgs[] =
  144. {
  145.     WM_KEYDOWN, WM_SYSKEYDOWN,  WM_TIMER,
  146.     WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN,
  147.     WM_NCLBUTTONDOWN, WM_NCRBUTTONDOWN, WM_NCMBUTTONDOWN,
  148.     WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK,
  149.     WM_NCLBUTTONDBLCLK, WM_NCRBUTTONDBLCLK, WM_NCMBUTTONDBLCLK
  150. };
  151.  
  152. BOOL COleMessageFilter::IsSignificantMessage(MSG*)
  153. {
  154.     // check for "significant" messages in the queue
  155.     MSG msg;
  156.     for (int i = 0; i < _countof(_afxSignificantMsgs); i++)
  157.     {
  158.         if (::PeekMessage(&msg, NULL, _afxSignificantMsgs[i], _afxSignificantMsgs[i],
  159.             PM_NOREMOVE|PM_NOYIELD))
  160.         {
  161.             if ((msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) &&
  162.                 (HIWORD(msg.lParam) & KF_REPEAT))
  163.             {
  164.                 // a key-repeat is a non-significant message
  165.                 continue;
  166.             }
  167.  
  168.             // "significant" message is waiting in the queue
  169.             return TRUE;
  170.         }
  171.     }
  172.  
  173.     // no significant messages in the queue
  174.     return FALSE;
  175. }
  176.  
  177. int COleMessageFilter::OnBusyDialog(HTASK htaskBusy)
  178. {
  179.     COleBusyDialog dlg(htaskBusy, FALSE);
  180.  
  181.     int nResult = -1;
  182.     TRY
  183.     {
  184.         if (dlg.DoModal() == IDOK)
  185.             nResult = dlg.GetSelectionType();
  186.     }
  187.     END_TRY
  188.  
  189.     return nResult;
  190. }
  191.  
  192. int COleMessageFilter::OnNotRespondingDialog(HTASK htaskBusy)
  193. {
  194.     COleBusyDialog dlg(htaskBusy, TRUE);
  195.  
  196.     int nResult = -1;
  197.     TRY
  198.     {
  199.         if (dlg.DoModal() == IDOK)
  200.             nResult = dlg.GetSelectionType();
  201.     }
  202.     END_TRY
  203.  
  204.     return nResult;
  205. }
  206.  
  207. /////////////////////////////////////////////////////////////////////////////
  208. // COleMessageFilter OLE interface implementation
  209.  
  210. BEGIN_INTERFACE_MAP(COleMessageFilter, CCmdTarget)
  211.     INTERFACE_PART(COleMessageFilter, IID_IMessageFilter, MessageFilter)
  212. END_INTERFACE_MAP()
  213.  
  214. STDMETHODIMP_(ULONG) COleMessageFilter::XMessageFilter::AddRef()
  215. {
  216.     METHOD_PROLOGUE_EX_(COleMessageFilter, MessageFilter)
  217.     return pThis->ExternalAddRef();
  218. }
  219.  
  220. STDMETHODIMP_(ULONG) COleMessageFilter::XMessageFilter::Release()
  221. {
  222.     METHOD_PROLOGUE_EX_(COleMessageFilter, MessageFilter)
  223.     return pThis->ExternalRelease();
  224. }
  225.  
  226. STDMETHODIMP COleMessageFilter::XMessageFilter::QueryInterface(
  227.     REFIID iid, LPVOID* ppvObj)
  228. {
  229.     METHOD_PROLOGUE_EX_(COleMessageFilter, MessageFilter)
  230.     return pThis->ExternalQueryInterface(&iid, ppvObj);
  231. }
  232.  
  233. STDMETHODIMP_(DWORD) COleMessageFilter::XMessageFilter::HandleInComingCall(
  234.     DWORD dwCallType, HTASK /*htaskCaller*/,
  235.     DWORD /*dwTickCount*/, LPINTERFACEINFO /*lpInterfaceInfo*/)
  236. {
  237.     METHOD_PROLOGUE_EX_(COleMessageFilter, MessageFilter)
  238.  
  239.     // check for application busy first...
  240.     if (pThis->m_nBusyCount == 0)
  241.     {
  242.         if (dwCallType == CALLTYPE_TOPLEVEL ||
  243.             dwCallType == CALLTYPE_TOPLEVEL_CALLPENDING)
  244.         {
  245.             // make sure CWinThread::OnIdle has a chance to run later
  246.             MSG msg;
  247.             if (!::PeekMessage(&msg, NULL, WM_KICKIDLE, WM_KICKIDLE, PM_NOREMOVE))
  248.                 ::PostThreadMessage(GetCurrentThreadId(), WM_KICKIDLE, 0, 0);
  249.         }
  250.         return SERVERCALL_ISHANDLED;
  251.     }
  252.  
  253.     if (dwCallType == CALLTYPE_TOPLEVEL ||
  254.         dwCallType == CALLTYPE_TOPLEVEL_CALLPENDING)
  255.     {
  256.         // application is busy and we have rejectable CALLTYPE
  257.         return pThis->m_nBusyReply;
  258.     }
  259.  
  260.     // application is busy, but CALLTYPE indicates that it *must* be handled
  261.     return SERVERCALL_ISHANDLED;
  262. }
  263.  
  264. STDMETHODIMP_(DWORD) COleMessageFilter::XMessageFilter::RetryRejectedCall(
  265.     HTASK htaskCallee, DWORD dwTickCount, DWORD dwRejectType)
  266. {
  267.     METHOD_PROLOGUE_EX(COleMessageFilter, MessageFilter)
  268.     ASSERT_VALID(pThis);
  269.  
  270.     // rejected calls get cancelled regardless of timeout
  271.     if (dwRejectType == SERVERCALL_REJECTED)
  272.         return (DWORD)-1;
  273.  
  274.     // if insignificant time has passed, don't panic -- just retry
  275.     if (dwTickCount <= pThis->m_nRetryReply)
  276.         return 0;   // retry right away (0-100 are retry immediate)
  277.  
  278.     // too much time has passed, do something more drastic
  279.     if (pThis->m_bEnableBusy)
  280.     {
  281.         // show busy dialog
  282.         int selType = pThis->OnBusyDialog(htaskCallee);
  283.  
  284.         // take action depending on selection
  285.         switch (selType)
  286.         {
  287.         case -1:
  288.             return (DWORD)-1;   // cancel outgoing call
  289.  
  290.         case COleBusyDialog::retry:
  291.             return 0;           // retry immediately
  292.         }
  293.     }
  294.     return pThis->m_nRetryReply;    // use standard retry timeout
  295. }
  296.  
  297. STDMETHODIMP_(DWORD) COleMessageFilter::XMessageFilter::MessagePending(
  298.     HTASK htaskCallee, DWORD dwTickCount, DWORD /*dwPendingType*/)
  299. {
  300.     METHOD_PROLOGUE_EX(COleMessageFilter, MessageFilter)
  301.     ASSERT_VALID(pThis);
  302.  
  303.     MSG msg;
  304.     if (dwTickCount > pThis->m_nTimeout && !pThis->m_bUnblocking &&
  305.         pThis->IsSignificantMessage(&msg))
  306.     {
  307.         if (pThis->m_bEnableNotResponding)
  308.         {
  309.             pThis->m_bUnblocking = TRUE;    // avoid reentrant calls
  310.  
  311.             // eat all mouse messages in our queue
  312.             while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,
  313.                 PM_REMOVE|PM_NOYIELD))
  314.                 ;
  315.             // eat all keyboard messages in our queue
  316.             while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,
  317.                 PM_REMOVE|PM_NOYIELD))
  318.                 ;
  319.  
  320.             // show not responding dialog
  321.             pThis->OnNotRespondingDialog(htaskCallee);
  322.             pThis->m_bUnblocking = FALSE;
  323.  
  324.             return PENDINGMSG_WAITNOPROCESS;
  325.         }
  326.     }
  327.  
  328.     // don't process re-entrant messages
  329.     if (pThis->m_bUnblocking)
  330.         return PENDINGMSG_WAITDEFPROCESS;
  331.  
  332.     // allow application to process pending message
  333.     if (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE|PM_NOYIELD))
  334.         pThis->OnMessagePending(&msg);
  335.  
  336.     // by default we return pending MSG wait
  337.     return PENDINGMSG_WAITNOPROCESS;
  338. }
  339.  
  340. /////////////////////////////////////////////////////////////////////////////
  341. // COleMessageFilter diagnostics
  342.  
  343. #ifdef _DEBUG
  344. void COleMessageFilter::AssertValid() const
  345. {
  346.     CCmdTarget::AssertValid();
  347. }
  348.  
  349. void COleMessageFilter::Dump(CDumpContext& dc) const
  350. {
  351.     CCmdTarget::Dump(dc);
  352.  
  353.     dc << "m_bRegistered = " << m_bRegistered;
  354.     dc << "\nm_nBusyCount = " << m_nBusyCount;
  355.     dc << "\nm_bEnableBusy = " << m_bEnableBusy;
  356.     dc << "\nm_bEnableNotResponding = " << m_bEnableNotResponding;
  357.     dc << "\nm_bUnblocking = " << m_bUnblocking;
  358.     dc << "\nm_nRetryReply = " << m_nRetryReply;
  359.     dc << "\nm_nBusyReply = " << m_nBusyReply;
  360.     dc << "\nm_nTimeout = " << m_nTimeout;
  361.  
  362.     dc << "\n";
  363. }
  364. #endif
  365.  
  366. /////////////////////////////////////////////////////////////////////////////
  367.