olemsgf.cpp 9.1 KB

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