Source code of Windows XP (NT5)
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.

455 lines
9.1 KiB

  1. //=================================================================
  2. //
  3. // WinMsgEvent.cpp --
  4. //
  5. // Copyright (c) 2000-2001 Microsoft Corporation, All Rights Reserved
  6. //
  7. //=================================================================
  8. #include "precomp.h"
  9. #include <lockwrap.h>
  10. #include "WinMsgEvent.h"
  11. // initialize class globals
  12. CCritSec CWinMsgEvent::mg_csMapLock ;
  13. CCritSec CWinMsgEvent::mg_csWindowLock ;
  14. CAutoEvent CWinMsgEvent::mg_aeCreateWindow ;
  15. CWinMsgEvent::Sink_Map CWinMsgEvent::mg_oSinkMap ;
  16. HANDLE CWinMsgEvent::mg_hThreadPumpHandle = NULL;
  17. HWND CWinMsgEvent::mg_hWnd = NULL;
  18. #define EVENT_MAP_LOCK_ CLockWrapper t_oAcs( mg_csMapLock ) ;
  19. #define WINDOW_LOCK_ CLockWrapper t_oAcs( mg_csWindowLock ) ;
  20. // per object call
  21. CWinMsgEvent::CWinMsgEvent()
  22. {}
  23. // per object call
  24. CWinMsgEvent::~CWinMsgEvent()
  25. {
  26. UnRegisterAllMessages() ;
  27. // clear the WM_ENDSESSION handler
  28. if( mg_oSinkMap.end() == mg_oSinkMap.find( WM_ENDSESSION ) )
  29. {
  30. // Note: If WM_ENDSESSION was never IN the map, this
  31. // call will return zero (failed). However, it won't
  32. // do anything bad, so just ignore it.
  33. SetConsoleCtrlHandler( &CtrlHandlerRoutine, FALSE ) ;
  34. }
  35. }
  36. // per object call
  37. void CWinMsgEvent::RegisterForMessage(
  38. IN UINT a_message
  39. )
  40. {
  41. BOOL t_bFound = FALSE ;
  42. BOOL t_bCreateWindow = FALSE ;
  43. { EVENT_MAP_LOCK_
  44. if( mg_oSinkMap.empty() )
  45. {
  46. t_bCreateWindow = TRUE ;
  47. }
  48. else // lookup for message/object duplicate
  49. {
  50. CWinMsgEvent::Sink_Map::iterator t_SinkIter ;
  51. t_SinkIter = mg_oSinkMap.find( a_message ) ;
  52. while( t_SinkIter != mg_oSinkMap.end() )
  53. {
  54. if( a_message == t_SinkIter->first )
  55. {
  56. if( this == t_SinkIter->second )
  57. {
  58. t_bFound = TRUE ;
  59. break ;
  60. }
  61. ++t_SinkIter ;
  62. }
  63. else
  64. {
  65. break ;
  66. }
  67. }
  68. }
  69. if( !t_bFound )
  70. {
  71. // Set up a handler to simulate this message
  72. // as we won't get it running under local system account.
  73. if( WM_ENDSESSION == a_message &&
  74. mg_oSinkMap.end() == mg_oSinkMap.find( WM_ENDSESSION ) )
  75. {
  76. SetConsoleCtrlHandler( &CtrlHandlerRoutine, TRUE ) ;
  77. }
  78. // map the desired message for this object instance
  79. mg_oSinkMap.insert(
  80. pair<UINT const, CWinMsgEvent*>
  81. ( a_message,
  82. this ) ) ;
  83. }
  84. }
  85. if( t_bCreateWindow )
  86. {
  87. CreateMsgProvider() ;
  88. }
  89. }
  90. // per object call
  91. bool CWinMsgEvent::UnRegisterMessage(
  92. IN UINT a_message
  93. )
  94. {
  95. bool t_bRet = false ;
  96. BOOL t_bDestroyWindow = FALSE ;
  97. { EVENT_MAP_LOCK_
  98. CWinMsgEvent::Sink_Map::iterator t_SinkIter ;
  99. t_SinkIter = mg_oSinkMap.find( a_message ) ;
  100. while( t_SinkIter != mg_oSinkMap.end() )
  101. {
  102. if( a_message == t_SinkIter->first )
  103. {
  104. if( this == t_SinkIter->second )
  105. {
  106. t_SinkIter = mg_oSinkMap.erase( t_SinkIter ) ;
  107. t_bRet = true ;
  108. break;
  109. }
  110. else
  111. {
  112. t_SinkIter++;
  113. }
  114. }
  115. else
  116. {
  117. break ;
  118. }
  119. }
  120. if( mg_oSinkMap.empty() )
  121. {
  122. t_bDestroyWindow = TRUE ;
  123. }
  124. }
  125. if( t_bDestroyWindow )
  126. {
  127. DestroyMsgWindow() ;
  128. }
  129. return t_bRet ;
  130. }
  131. // per object call
  132. void CWinMsgEvent::UnRegisterAllMessages()
  133. {
  134. BOOL t_bDestroyWindow = FALSE ;
  135. { // Used for scoping the lock
  136. EVENT_MAP_LOCK_
  137. CWinMsgEvent::Sink_Map::iterator t_SinkIter ;
  138. t_SinkIter = mg_oSinkMap.begin() ;
  139. while( t_SinkIter != mg_oSinkMap.end() )
  140. {
  141. if( this == t_SinkIter->second )
  142. {
  143. t_SinkIter = mg_oSinkMap.erase( t_SinkIter ) ;
  144. }
  145. else
  146. {
  147. t_SinkIter++;
  148. }
  149. }
  150. if( mg_oSinkMap.empty() )
  151. {
  152. t_bDestroyWindow = TRUE;
  153. }
  154. }
  155. if( t_bDestroyWindow )
  156. {
  157. DestroyMsgWindow() ;
  158. }
  159. }
  160. // global private
  161. void CWinMsgEvent::CreateMsgProvider()
  162. {
  163. WINDOW_LOCK_
  164. if( NULL == mg_hThreadPumpHandle )
  165. {
  166. DWORD t_dwThreadID ;
  167. // Create a thread that will spin off a windowed msg pump
  168. mg_hThreadPumpHandle = CreateThread(
  169. NULL, // pointer to security attributes
  170. 0L, // initial thread stack size
  171. dwThreadProc, // pointer to thread function
  172. 0L, // argument for new thread
  173. 0L, // creation flags
  174. &t_dwThreadID ) ;
  175. // wait for async window create
  176. mg_aeCreateWindow.Wait( INFINITE );
  177. if( !mg_hWnd )
  178. {
  179. CloseHandle( mg_hThreadPumpHandle ) ;
  180. mg_hThreadPumpHandle = NULL ;
  181. }
  182. }
  183. }
  184. //
  185. void CWinMsgEvent::DestroyMsgWindow()
  186. {
  187. WINDOW_LOCK_
  188. HANDLE t_hThreadPumpHandle = mg_hThreadPumpHandle ;
  189. HWND t_hWnd = mg_hWnd ;
  190. // clear globals
  191. mg_hThreadPumpHandle = NULL ;
  192. mg_hWnd = NULL ;
  193. if( t_hWnd )
  194. {
  195. SendMessage( t_hWnd, WM_CLOSE, 0, 0 ) ;
  196. }
  197. if( t_hThreadPumpHandle )
  198. {
  199. WaitForSingleObject(
  200. t_hThreadPumpHandle,
  201. 20000
  202. );
  203. CloseHandle( t_hThreadPumpHandle ) ;
  204. }
  205. }
  206. BOOL WINAPI CWinMsgEvent::CtrlHandlerRoutine(DWORD dwCtrlType)
  207. {
  208. HWND t_hWnd = NULL ;
  209. UINT t_message = 0 ;
  210. WPARAM t_wParam = 0 ;
  211. LPARAM t_lParam = 0 ;
  212. // simulate the message
  213. if( CTRL_LOGOFF_EVENT == dwCtrlType )
  214. {
  215. t_message = WM_ENDSESSION ;
  216. t_wParam = TRUE ; // session ending
  217. t_lParam = ENDSESSION_LOGOFF ; // Logoff event
  218. }
  219. else if( CTRL_SHUTDOWN_EVENT == dwCtrlType )
  220. {
  221. t_message = WM_ENDSESSION ;
  222. t_wParam = TRUE ; // session ending
  223. t_lParam = 0 ; // Shutdown event
  224. }
  225. if( t_message )
  226. {
  227. //
  228. MsgWndProc( t_hWnd,
  229. t_message,
  230. t_wParam,
  231. t_lParam ) ;
  232. }
  233. return FALSE; // Pass event on to next handler.
  234. }
  235. // worker thread pump, global private
  236. DWORD WINAPI CWinMsgEvent::dwThreadProc( LPVOID a_lpParameter )
  237. {
  238. DWORD t_dwRet = FALSE ;
  239. if( CreateMsgWindow() )
  240. {
  241. WindowsDispatch() ;
  242. t_dwRet = TRUE ;
  243. }
  244. UnregisterClass( MSGWINDOWNAME, GetModuleHandle(NULL) ) ;
  245. return t_dwRet ;
  246. }
  247. // global private
  248. HWND CWinMsgEvent::CreateMsgWindow()
  249. {
  250. DWORD t_Err = 0;
  251. HMODULE t_hMod = GetModuleHandle(NULL);
  252. if (t_hMod != NULL)
  253. {
  254. WNDCLASS wndclass ;
  255. wndclass.style = 0 ;
  256. wndclass.lpfnWndProc = MsgWndProc ;
  257. wndclass.cbClsExtra = 0 ;
  258. wndclass.cbWndExtra = 0 ;
  259. wndclass.hInstance = t_hMod ;
  260. wndclass.hIcon = NULL ;
  261. wndclass.hCursor = NULL ;
  262. wndclass.hbrBackground = NULL ;
  263. wndclass.lpszMenuName = NULL ;
  264. wndclass.lpszClassName = MSGWINDOWNAME ;
  265. RegisterClass( &wndclass ) ;
  266. mg_hWnd = CreateWindowEx( WS_EX_TOPMOST,
  267. MSGWINDOWNAME,
  268. TEXT("WinMsgEventProv"),
  269. WS_OVERLAPPED,
  270. CW_USEDEFAULT,
  271. CW_USEDEFAULT,
  272. CW_USEDEFAULT,
  273. CW_USEDEFAULT,
  274. NULL,
  275. NULL,
  276. t_hMod,
  277. NULL ) ;
  278. }
  279. else
  280. {
  281. t_Err = GetLastError();
  282. }
  283. mg_aeCreateWindow.Signal();
  284. return mg_hWnd ;
  285. }
  286. // global private
  287. void CWinMsgEvent::WindowsDispatch()
  288. {
  289. BOOL t_GetMessage ;
  290. MSG t_lpMsg ;
  291. while ( ( t_GetMessage = GetMessage ( &t_lpMsg , NULL , 0 , 0 ) ) == TRUE )
  292. {
  293. TranslateMessage ( &t_lpMsg ) ;
  294. DispatchMessage ( &t_lpMsg ) ;
  295. }
  296. }
  297. // global private
  298. LRESULT CALLBACK CWinMsgEvent::MsgWndProc(
  299. IN HWND a_hWnd,
  300. IN UINT a_message,
  301. IN WPARAM a_wParam,
  302. IN LPARAM a_lParam
  303. )
  304. {
  305. LRESULT t_lResult = TRUE ;
  306. E_ReturnAction t_eReturnAction = e_DefProc ;
  307. switch ( a_message )
  308. {
  309. default:
  310. {
  311. // Run through the message map
  312. // If registered requestor(s) are found dispatch it...
  313. EVENT_MAP_LOCK_
  314. CWinMsgEvent::Sink_Map::iterator t_SinkIter ;
  315. t_SinkIter = mg_oSinkMap.find( a_message ) ;
  316. while( t_SinkIter != mg_oSinkMap.end() )
  317. {
  318. if( a_message == t_SinkIter->first )
  319. {
  320. // signal
  321. t_SinkIter->second->WinMsgEvent(
  322. a_hWnd,
  323. a_message,
  324. a_wParam,
  325. a_lParam,
  326. t_eReturnAction,
  327. t_lResult ) ;
  328. ++t_SinkIter ;
  329. }
  330. else
  331. {
  332. break ;
  333. }
  334. }
  335. // special return processing ---
  336. //
  337. // The default is to defer to DefWindowProc.
  338. // However, multiple sinks can exist for a message.
  339. // Each may require special return processing.
  340. //
  341. // Example: WM_POWERBROADCAST submessage PBT_APMQUERYSUSPEND requires
  342. // the returning of TRUE to indicate interest in additional Power
  343. // Event messages. This a passive request ( asking for additional info )
  344. // but another sink registered for this message may have a different opinion.
  345. // Trivial perhaps, but other message processing may be different; placing
  346. // the requestor at odds with the intent of another.
  347. // Behavior here: All sinks are called with the
  348. // updated t_eReturnAction from the last sink call.
  349. // If a sink suspects it would have to act diffently based on specific
  350. // knowledge of message usage the sink will have to instead spin off
  351. // its own window to handle the special return and not make use of this
  352. // generalized class.
  353. //
  354. if( e_DefProc == t_eReturnAction )
  355. {
  356. t_lResult = DefWindowProc( a_hWnd, a_message, a_wParam, a_lParam ) ;
  357. }
  358. break ;
  359. }
  360. case WM_CLOSE:
  361. {
  362. if ( mg_hWnd != NULL)
  363. {
  364. t_lResult = 0;
  365. }
  366. else
  367. {
  368. t_lResult = DefWindowProc( a_hWnd, a_message, a_wParam, a_lParam ) ;
  369. }
  370. break;
  371. }
  372. case WM_DESTROY:
  373. {
  374. PostMessage ( a_hWnd, WM_QUIT, 0, 0 ) ;
  375. t_lResult = 0;
  376. }
  377. break ;
  378. }
  379. return t_lResult;
  380. }