Leaked source code of windows server 2003
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.

696 lines
21 KiB

  1. /*--------------------------------------------------------------------------*
  2. *
  3. * Microsoft Windows
  4. * Copyright (C) Microsoft Corporation, 1992 - 000
  5. *
  6. * File: power.cpp
  7. *
  8. * Contents: Implementation file for CConsolePower
  9. *
  10. * History: 25-Feb-2000 jeffro Created
  11. *
  12. *--------------------------------------------------------------------------*/
  13. #include <stdafx.h>
  14. #include "power.h"
  15. /*
  16. * allocate a TLS index for CConsolePower objects
  17. */
  18. const DWORD CConsolePower::s_dwTlsIndex = TlsAlloc();
  19. const DWORD CConsolePower::s_rgExecStateFlag[CConsolePower::eIndex_Count] =
  20. {
  21. ES_SYSTEM_REQUIRED, // eIndex_System
  22. ES_DISPLAY_REQUIRED, // eIndex_Display
  23. };
  24. const CConsolePower::ExecutionStateFunc CConsolePower::s_FuncUninitialized =
  25. (ExecutionStateFunc)(LONG_PTR) -1;
  26. CConsolePower::ExecutionStateFunc CConsolePower::SetThreadExecutionState_ =
  27. s_FuncUninitialized;
  28. /*+-------------------------------------------------------------------------*
  29. * ScPrepExecutionStateFlag
  30. *
  31. * This function increments or decrements the count for dwTestBit for this
  32. * object, and for the thread. On exit dwSTESArg contains the appropriate
  33. * flag to pass to SetThreadExecutionState for dwTestBit. That is, if the
  34. * thread count for dwTestBit is non-zero, dwSTESArg will contain dwTestBit
  35. * on return; if the thread count is zero, dwSTESArg will not contain
  36. * dwTestBit.
  37. *--------------------------------------------------------------------------*/
  38. static SC ScPrepExecutionStateFlag (
  39. DWORD dwTestBit, /* I:single ES_* flag to test */
  40. DWORD dwAdd, /* I:flags to add */
  41. DWORD dwRemove, /* I:flags to remove */
  42. DWORD * pdwSTESArg, /* I/O:arg to SetThreadExecutionState */
  43. LONG * pcObjectRequests, /* I/O:request count for this object */
  44. LONG * pcThreadRequests, /* I/O:request count for this thread */
  45. UINT cIterations = 1) /* I:times to add/remove dwTestBit */
  46. {
  47. DECLARE_SC (sc, _T("ScPrepExecutionStateFlag"));
  48. /*
  49. * validate inputs -- DO NOT clear these output variables, the
  50. * existing values are modified here
  51. */
  52. sc = ScCheckPointers (pdwSTESArg, pcObjectRequests, pcThreadRequests);
  53. if (sc)
  54. return (sc);
  55. /*
  56. * make sure the bit isn't to be both removed and added
  57. */
  58. if ((dwAdd & dwTestBit) && (dwRemove & dwTestBit))
  59. return (sc = E_INVALIDARG);
  60. /*
  61. * We should always have a non-negative number of requests for the bit
  62. * under test for this object, and at least as many requests for the
  63. * thread as we do for this object.
  64. */
  65. ASSERT (*pcObjectRequests >= 0);
  66. ASSERT (*pcThreadRequests >= *pcObjectRequests);
  67. if ((*pcObjectRequests < 0) || (*pcThreadRequests < *pcObjectRequests))
  68. return (sc = E_UNEXPECTED);
  69. /*
  70. * If we're adding the test bit, bump up the request count for this
  71. * object and this thread.
  72. */
  73. if (dwAdd & dwTestBit)
  74. {
  75. *pcObjectRequests += cIterations;
  76. *pcThreadRequests += cIterations;
  77. }
  78. /*
  79. * Otherwise, if we're removing the test bit, bump down the request counts
  80. * for this object and this thread.
  81. */
  82. else if (dwRemove & dwTestBit)
  83. {
  84. /*
  85. * Can't remove the bit under test if we don't have an outstanding
  86. * request for it on this object.
  87. */
  88. if (*pcObjectRequests < cIterations)
  89. return (sc = E_INVALIDARG);
  90. *pcObjectRequests -= cIterations;
  91. *pcThreadRequests -= cIterations;
  92. }
  93. /*
  94. * If the net count for this thread is non-zero, the bit under
  95. * test needs to be in the argument for SetThreadExecutionState;
  96. * if not, the bit under test needs to be removed.
  97. */
  98. if (*pcThreadRequests != 0)
  99. *pdwSTESArg |= dwTestBit;
  100. else
  101. *pdwSTESArg &= ~dwTestBit;
  102. return (sc);
  103. }
  104. /*+-------------------------------------------------------------------------*
  105. * CConsolePower::CConsolePower
  106. *
  107. * Constructs a CConsolePower object.
  108. *--------------------------------------------------------------------------*/
  109. CConsolePower::CConsolePower () :
  110. m_wndPower (this)
  111. {
  112. DEBUG_INCREMENT_INSTANCE_COUNTER(CConsolePower);
  113. /*
  114. * If any of these fail, s_rgExecStateFlag is out of order. It would
  115. * be better to use COMPILETIME_ASSERT here, but using that gives us
  116. *
  117. * error C2051: case expression not constant
  118. *
  119. * Bummer.
  120. */
  121. ASSERT (s_rgExecStateFlag[eIndex_System] == ES_SYSTEM_REQUIRED);
  122. ASSERT (s_rgExecStateFlag[eIndex_Display] == ES_DISPLAY_REQUIRED);
  123. }
  124. /*+-------------------------------------------------------------------------*
  125. * CConsolePower::~CConsolePower
  126. *
  127. * Destroys a CConsolePower object. If this object holds references to
  128. * ES_SYSTEM_REQUIRED or ES_DISPLAY_REQUIRED settings, they will be cleared.
  129. *--------------------------------------------------------------------------*/
  130. CConsolePower::~CConsolePower ()
  131. {
  132. DECLARE_SC (sc, _T("CConsolePower::~CConsolePower"));
  133. DEBUG_DECREMENT_INSTANCE_COUNTER(CConsolePower);
  134. /*
  135. * clean up outstanding references, if any
  136. */
  137. if (!IsBadCodePtr ((FARPROC) SetThreadExecutionState_) &&
  138. ((m_Counts.m_rgCount[eIndex_System] != 0) ||
  139. (m_Counts.m_rgCount[eIndex_Display] != 0)))
  140. {
  141. try
  142. {
  143. /*
  144. * get the thread counts
  145. */
  146. sc = ScCheckPointers (m_spThreadCounts, E_UNEXPECTED);
  147. if (sc)
  148. sc.Throw();
  149. DWORD dwFlags = ES_CONTINUOUS;
  150. /*
  151. * clean up each individual count
  152. */
  153. for (int i = 0; i < eIndex_Count; i++)
  154. {
  155. /*
  156. * prevent underflow
  157. */
  158. if (m_Counts.m_rgCount[i] > m_spThreadCounts->m_rgCount[i])
  159. (sc = E_UNEXPECTED).Throw();
  160. sc = ScPrepExecutionStateFlag (s_rgExecStateFlag[i], // dwTestBit
  161. 0, // dwAdd
  162. s_rgExecStateFlag[i], // dwRemove
  163. &dwFlags,
  164. &m_Counts.m_rgCount[i],
  165. &m_spThreadCounts->m_rgCount[i],
  166. m_Counts.m_rgCount[i]);
  167. if (sc)
  168. sc.Throw();
  169. }
  170. /*
  171. * clean up the execution state for this thread
  172. */
  173. if (!SetThreadExecutionState_(dwFlags))
  174. {
  175. sc.FromLastError();
  176. sc.Throw();
  177. }
  178. }
  179. catch (SC& scCaught)
  180. {
  181. sc = scCaught;
  182. }
  183. }
  184. }
  185. /*+-------------------------------------------------------------------------*
  186. * CConsolePower::FinalConstruct
  187. *
  188. * This isn't the typical use of FinalConstruct in ATL objects. It is
  189. * typically used for creating aggregated objects, but using it in this
  190. * way allows us to prevent the creation of CConsolePower objects without
  191. * throwing an exception from the ctor, which ATL can't handle.
  192. *--------------------------------------------------------------------------*/
  193. HRESULT CConsolePower::FinalConstruct ()
  194. {
  195. DECLARE_SC (sc, _T("CConsolePower::FinalConstruct"));
  196. /*
  197. * if this is the first time a CConsolePower has been created, try to
  198. * dynaload SetThreadExecutionState (it's not supported on WinNT and
  199. * Win95)
  200. */
  201. if (SetThreadExecutionState_ == s_FuncUninitialized)
  202. {
  203. SetThreadExecutionState_ =
  204. (ExecutionStateFunc) GetProcAddress (
  205. GetModuleHandle (_T("kernel32.dll")),
  206. "SetThreadExecutionState");
  207. }
  208. /*
  209. * if SetThreadExecutionState is supported on this platform, do the
  210. * other initialization we'll need
  211. */
  212. if (!IsBadCodePtr ((FARPROC) SetThreadExecutionState_))
  213. {
  214. /*
  215. * if we couldn't get the thread-local CRefCountedTlsExecutionCounts
  216. * object for this thread, CConsolePower is useless, so fail creation
  217. */
  218. sc = ScGetThreadCounts (&m_spThreadCounts);
  219. if (sc)
  220. return (sc.ToHr());
  221. sc = ScCheckPointers (m_spThreadCounts, E_UNEXPECTED);
  222. if (sc)
  223. return (sc.ToHr());
  224. /*
  225. * create the window to handle WM_POWERBROADCAST
  226. */
  227. sc = m_wndPower.ScCreate ();
  228. if (sc)
  229. return (sc.ToHr());
  230. }
  231. return (sc.ToHr());
  232. }
  233. /*+-------------------------------------------------------------------------*
  234. * CConsolePower::ScGetThreadCounts
  235. *
  236. * Returns a pointer to the thread-local CRefCountedTlsExecutionCounts object
  237. * for this thread, allocating one if necessary.
  238. *
  239. * NOTE: The returned pointer has a reference added. It is the client's
  240. * responsibility to release the reference.
  241. *--------------------------------------------------------------------------*/
  242. SC CConsolePower::ScGetThreadCounts (CRefCountedTlsExecutionCounts** ppThreadCounts)
  243. {
  244. DECLARE_SC (sc, _T("CConsolePower::ScGetThreadCounts"));
  245. /*
  246. * we shouldn't get here if we're on a platform that doesn't support
  247. * SetThreadExecutionState
  248. */
  249. ASSERT (!IsBadCodePtr ((FARPROC) SetThreadExecutionState_));
  250. if (IsBadCodePtr ((FARPROC) SetThreadExecutionState_))
  251. return (sc = E_UNEXPECTED);
  252. sc = ScCheckPointers (ppThreadCounts);
  253. if (sc)
  254. return (sc);
  255. /*
  256. * init output
  257. */
  258. (*ppThreadCounts) = NULL;
  259. /*
  260. * couldn't allocate a TLS index? fail
  261. */
  262. if (s_dwTlsIndex == TLS_OUT_OF_INDEXES)
  263. return (sc = E_OUTOFMEMORY);
  264. /*
  265. * Get the existing thread counts structure. If this is the first
  266. * time through (i.e. the first CConsolePower created on this thread),
  267. * we won't have a thread counts structure, so we'll allocate one now.
  268. */
  269. CTlsExecutionCounts* pTEC = CTlsExecutionCounts::GetThreadInstance(s_dwTlsIndex);
  270. if (pTEC == NULL)
  271. {
  272. /*
  273. * allocate the struct for this thread
  274. */
  275. (*ppThreadCounts) = CRefCountedTlsExecutionCounts::CreateInstance();
  276. if ((*ppThreadCounts) == NULL)
  277. return (sc = E_OUTOFMEMORY);
  278. /*
  279. * put it in our TLS slot
  280. */
  281. sc = (*ppThreadCounts)->ScSetThreadInstance (s_dwTlsIndex);
  282. if (sc)
  283. return (sc);
  284. }
  285. else
  286. (*ppThreadCounts) = static_cast<CRefCountedTlsExecutionCounts*>(pTEC);
  287. /*
  288. * put a reference on for the caller
  289. */
  290. (*ppThreadCounts)->AddRef();
  291. return (sc);
  292. }
  293. /*+-------------------------------------------------------------------------*
  294. * CConsolePower::SetExecutionState
  295. *
  296. * This method wraps the ::SetThreadExecutionState API in a manner that is
  297. * safe in the presence of multiple COM servers (i.e. snap-ins) that might
  298. * need to call ::SetThreadExecutionState.
  299. *
  300. * The problem is that ::SetThreadExecutionState doesn't maintain reference
  301. * counts on the flags that it is passed. For instance:
  302. *
  303. * SetThreadExecutionState (ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
  304. * SetThreadExecutionState (ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
  305. * SetThreadExecutionState (ES_CONTINUOUS);
  306. *
  307. * will result in the ES_SYSTEM_REQUIRED bit being off, even though it was
  308. * set twice and only cleared once. This can lead to conflicts between
  309. * snap-ins, like in this scenario:
  310. *
  311. * SnapinA:
  312. * SetThreadExecutionState (ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
  313. *
  314. * SnapinB:
  315. * SetThreadExecutionState (ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
  316. * SetThreadExecutionState (ES_CONTINUOUS);
  317. *
  318. * (a Long Time passes)
  319. *
  320. * SnapinA:
  321. * SetThreadExecutionState (ES_CONTINUOUS);
  322. *
  323. * Because of the nature of SetThreadExecutionState, during
  324. * the Long Time, SnapinA thinks the ES_SYSTEM_REQUIRED bit is set, even
  325. * though SnapinB has turned it off.
  326. *
  327. * The CConsolePower object maintains a per-snap-in count of the execution
  328. * state bits, so they can all happily coexist.
  329. *--------------------------------------------------------------------------*/
  330. STDMETHODIMP CConsolePower::SetExecutionState (
  331. DWORD dwAdd, /* I:flags to add */
  332. DWORD dwRemove) /* I:flags to remove */
  333. {
  334. DECLARE_SC (sc, _T("CConsolePower::SetExecutionState"));
  335. #ifdef DBG
  336. /*
  337. * this object is CoCreated so we can't tell what the snap-in name is
  338. */
  339. sc.SetSnapinName (_T("<unknown>"));
  340. #endif
  341. /*
  342. * if SetExecutionState isn't supported on this platform, don't do
  343. * anything, but still "succeed"
  344. */
  345. if (IsBadCodePtr ((FARPROC) SetThreadExecutionState_))
  346. return ((sc = S_FALSE).ToHr());
  347. const DWORD dwValidFlags = ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED;
  348. DWORD dwFlags = 0;
  349. /*
  350. * if either dwAdd or dwRemove contain flags we don't recognize
  351. * (including ES_CONTINUOUS, which we expect to get in fContinuous)
  352. * fail
  353. */
  354. if (((dwAdd | dwRemove) & ~dwValidFlags) != 0)
  355. return ((sc = E_INVALIDARG).ToHr());
  356. /*
  357. * if we didn't get any flags, fail
  358. */
  359. if ((dwAdd == 0) && (dwRemove == 0))
  360. return ((sc = E_INVALIDARG).ToHr());
  361. /*
  362. * make sure we've got our thread counts
  363. */
  364. sc = ScCheckPointers (m_spThreadCounts, E_UNEXPECTED);
  365. if (sc)
  366. return (sc.ToHr());
  367. dwFlags = ES_CONTINUOUS;
  368. /*
  369. * add/remove each individual flag
  370. */
  371. for (int i = 0; i < eIndex_Count; i++)
  372. {
  373. sc = ScPrepExecutionStateFlag (s_rgExecStateFlag[i], // dwTestBit
  374. dwAdd,
  375. dwRemove,
  376. &dwFlags,
  377. &m_Counts.m_rgCount[i],
  378. &m_spThreadCounts->m_rgCount[i]);
  379. if (sc)
  380. return (sc.ToHr());
  381. }
  382. /*
  383. * set the execution state for this thread
  384. */
  385. if (!SetThreadExecutionState_(dwFlags))
  386. sc.FromLastError().ToHr();
  387. return (sc.ToHr());
  388. }
  389. /*+-------------------------------------------------------------------------*
  390. * CConsolePower::ResetIdleTimer
  391. *
  392. * Simple wrapper for SetThreadExecutionState (without ES_CONTINUOUS).
  393. *--------------------------------------------------------------------------*/
  394. STDMETHODIMP CConsolePower::ResetIdleTimer (DWORD dwFlags)
  395. {
  396. DECLARE_SC (sc, _T("CConsolePower::ResetIdleTimer"));
  397. #ifdef DBG
  398. /*
  399. * this object is CoCreated so we can't tell what the snap-in name is
  400. */
  401. sc.SetSnapinName (_T("<unknown>"));
  402. #endif
  403. /*
  404. * if SetExecutionState isn't supported on this platform, don't do
  405. * anything, but still "succeed"
  406. */
  407. if (IsBadCodePtr ((FARPROC) SetThreadExecutionState_))
  408. return ((sc = S_FALSE).ToHr());
  409. /*
  410. * Set the execution state for this thread. SetThreadExecutionState
  411. * will do all parameter validation.
  412. */
  413. if (!SetThreadExecutionState_(dwFlags))
  414. sc.FromLastError().ToHr();
  415. return (sc.ToHr());
  416. }
  417. /*+-------------------------------------------------------------------------*
  418. * CConsolePower::OnPowerBroadcast
  419. *
  420. * WM_POWERBROADCAST handler for CConsolePower.
  421. *--------------------------------------------------------------------------*/
  422. LRESULT CConsolePower::OnPowerBroadcast (WPARAM wParam, LPARAM lParam)
  423. {
  424. /*
  425. * PBT_APMQUERYSUSPEND is the only event that the recipient can
  426. * deny. If a snap-in denies (by returning BROADCAST_QUERY_DENY),
  427. * there's no need to continue to fire the event to other snap-ins,
  428. * so we can break out and return the denial.
  429. */
  430. bool fBreakIfDenied = (wParam == PBT_APMQUERYSUSPEND);
  431. int cConnections = m_vec.GetSize();
  432. for (int i = 0; i < cConnections; i++)
  433. {
  434. CComQIPtr<IConsolePowerSink> spPowerSink = m_vec.GetAt(i);
  435. if (spPowerSink != NULL)
  436. {
  437. LRESULT lResult = TRUE;
  438. HRESULT hr = spPowerSink->OnPowerBroadcast (wParam, lParam, &lResult);
  439. /*
  440. * if the snap-in denied a PBT_APMQUERYSUSPEND, short out here
  441. */
  442. if (SUCCEEDED(hr) && fBreakIfDenied && (lResult == BROADCAST_QUERY_DENY))
  443. return (lResult);
  444. }
  445. }
  446. return (TRUE);
  447. }
  448. /*+-------------------------------------------------------------------------*
  449. * CConsolePower::CExecutionCounts::CExecutionCounts
  450. *
  451. * Constructs a CConsolePower::CExecutionCounts object.
  452. *--------------------------------------------------------------------------*/
  453. CConsolePower::CExecutionCounts::CExecutionCounts ()
  454. {
  455. for (int i = 0; i < countof (m_rgCount); i++)
  456. m_rgCount[i] = 0;
  457. }
  458. /*+-------------------------------------------------------------------------*
  459. * CConsolePower::CTlsExecutionCounts::CTlsExecutionCounts
  460. *
  461. * Constructs a CConsolePower::CTlsExecutionCounts object.
  462. *--------------------------------------------------------------------------*/
  463. CConsolePower::CTlsExecutionCounts::CTlsExecutionCounts () :
  464. m_dwTlsIndex (Uninitialized)
  465. {
  466. }
  467. /*+-------------------------------------------------------------------------*
  468. * CConsolePower::CTlsExecutionCounts::~CTlsExecutionCounts
  469. *
  470. * Destroys a CConsolePower::CTlsExecutionCounts object.
  471. *--------------------------------------------------------------------------*/
  472. CConsolePower::CTlsExecutionCounts::~CTlsExecutionCounts ()
  473. {
  474. if (m_dwTlsIndex != Uninitialized)
  475. TlsSetValue (m_dwTlsIndex, NULL);
  476. }
  477. /*+-------------------------------------------------------------------------*
  478. * CConsolePower::CTlsExecutionCounts::ScSetThreadInstance
  479. *
  480. * Accepts a valid TLS index and stores a pointer to this object in the
  481. * TLS slot identified by dwTlsIndex.
  482. *--------------------------------------------------------------------------*/
  483. SC CConsolePower::CTlsExecutionCounts::ScSetThreadInstance (DWORD dwTlsIndex)
  484. {
  485. DECLARE_SC (sc, _T("CConsolePower:CTlsExecutionCounts::ScSetThreadInstance"));
  486. /*
  487. * this can only be called once
  488. */
  489. ASSERT (m_dwTlsIndex == Uninitialized);
  490. if (m_dwTlsIndex != Uninitialized)
  491. return (sc = E_UNEXPECTED);
  492. /*
  493. * there shouldn't already be something in this slot
  494. */
  495. if (TlsGetValue (dwTlsIndex) != NULL)
  496. return (sc = E_UNEXPECTED);
  497. /*
  498. * save a pointer to ourselves in the TLS slot
  499. */
  500. if (!TlsSetValue (dwTlsIndex, this))
  501. return (sc.FromLastError());
  502. /*
  503. * save the TLS index
  504. */
  505. m_dwTlsIndex = dwTlsIndex;
  506. return (sc);
  507. }
  508. /*+-------------------------------------------------------------------------*
  509. * CConsolePower::CTlsExecutionCounts::GetThreadInstance
  510. *
  511. *
  512. *--------------------------------------------------------------------------*/
  513. CConsolePower::CTlsExecutionCounts*
  514. CConsolePower::CTlsExecutionCounts::GetThreadInstance (DWORD dwTlsIndex)
  515. {
  516. return ((CTlsExecutionCounts*) TlsGetValue (dwTlsIndex));
  517. }
  518. /*+-------------------------------------------------------------------------*
  519. * CConsolePowerWnd::CConsolePowerWnd
  520. *
  521. * Constructs a CConsolePowerWnd object.
  522. *--------------------------------------------------------------------------*/
  523. CConsolePowerWnd::CConsolePowerWnd (CConsolePower* pConsolePower) :
  524. m_pConsolePower(pConsolePower)
  525. {
  526. }
  527. /*+-------------------------------------------------------------------------*
  528. * CConsolePowerWnd::~CConsolePowerWnd
  529. *
  530. * Destroys a CConsolePowerWnd object.
  531. *--------------------------------------------------------------------------*/
  532. CConsolePowerWnd::~CConsolePowerWnd ()
  533. {
  534. /*
  535. * the Windows window for this class should never outlive the
  536. * C++ class that wraps it.
  537. */
  538. if (IsWindow ())
  539. DestroyWindow();
  540. }
  541. /*+-------------------------------------------------------------------------*
  542. * CConsolePowerWnd::ScCreate
  543. *
  544. * Creates the window for a CConsolePowerWnd object. This window will
  545. * handle WM_POWERBROADCAST.
  546. *--------------------------------------------------------------------------*/
  547. SC CConsolePowerWnd::ScCreate ()
  548. {
  549. DECLARE_SC (sc, _T("CConsolePowerWnd::ScCreate"));
  550. /*
  551. * create an invisible top-level window (only top-level windows receive
  552. * WM_POWERBROADCAST).
  553. */
  554. RECT rectEmpty = { 0, 0, 0, 0 };
  555. if (!Create (GetDesktopWindow(), rectEmpty))
  556. return (sc.FromLastError());
  557. return (sc);
  558. }
  559. /*+-------------------------------------------------------------------------*
  560. * CConsolePowerWnd::OnPowerBroadcast
  561. *
  562. * WM_POWERBROADCAST handler for CConsolePowerWnd.
  563. *--------------------------------------------------------------------------*/
  564. LRESULT CConsolePowerWnd::OnPowerBroadcast (
  565. UINT msg,
  566. WPARAM wParam,
  567. LPARAM lParam,
  568. BOOL& bHandled)
  569. {
  570. /*
  571. * if we aren't connected to a CConsolePower (shouldn't happen),
  572. * we can't handle the message
  573. */
  574. ASSERT (m_pConsolePower != NULL);
  575. if (m_pConsolePower == NULL)
  576. {
  577. bHandled = false;
  578. return (0);
  579. }
  580. return (m_pConsolePower->OnPowerBroadcast (wParam, lParam));
  581. }